1 /*
2 * Copyright 2019-present MongoDB, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "mongocrypt-ciphertext-private.h"
18 #include "mongocrypt-crypto-private.h"
19 #include "mongocrypt-ctx-private.h"
20 #include "mongocrypt-key-broker-private.h"
21 #include "mongocrypt-marking-private.h"
22 #include "mongocrypt-traverse-util-private.h"
23
24 /* Construct the list collections command to send. */
25 static bool
_mongo_op_collinfo(mongocrypt_ctx_t * ctx,mongocrypt_binary_t * out)26 _mongo_op_collinfo (mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out)
27 {
28 _mongocrypt_ctx_encrypt_t *ectx;
29 bson_t *cmd;
30
31 ectx = (_mongocrypt_ctx_encrypt_t *) ctx;
32 cmd = BCON_NEW ("name", BCON_UTF8 (ectx->coll_name));
33 CRYPT_TRACEF (&ectx->parent.crypt->log, "constructed: %s\n", tmp_json (cmd));
34 _mongocrypt_buffer_steal_from_bson (&ectx->list_collections_filter, cmd);
35 out->data = ectx->list_collections_filter.data;
36 out->len = ectx->list_collections_filter.len;
37 return true;
38 }
39
40 static bool
_set_schema_from_collinfo(mongocrypt_ctx_t * ctx,bson_t * collinfo)41 _set_schema_from_collinfo (mongocrypt_ctx_t *ctx, bson_t *collinfo)
42 {
43 bson_iter_t iter;
44 _mongocrypt_ctx_encrypt_t *ectx;
45 bool found_jsonschema = false;
46
47 /* Parse out the schema. */
48 ectx = (_mongocrypt_ctx_encrypt_t *) ctx;
49
50 /* Disallow views. */
51 if (bson_iter_init_find (&iter, collinfo, "type") &&
52 BSON_ITER_HOLDS_UTF8 (&iter) && bson_iter_utf8 (&iter, NULL) &&
53 0 == strcmp ("view", bson_iter_utf8 (&iter, NULL))) {
54 return _mongocrypt_ctx_fail_w_msg (ctx, "cannot auto encrypt a view");
55 }
56
57 if (!bson_iter_init (&iter, collinfo)) {
58 return _mongocrypt_ctx_fail_w_msg (ctx, "BSON malformed");
59 }
60
61 if (bson_iter_find_descendant (&iter, "options.validator", &iter) &&
62 BSON_ITER_HOLDS_DOCUMENT (&iter)) {
63 if (!bson_iter_recurse (&iter, &iter)) {
64 return _mongocrypt_ctx_fail_w_msg (ctx, "BSON malformed");
65 }
66 while (bson_iter_next (&iter)) {
67 const char *key;
68
69 key = bson_iter_key (&iter);
70 BSON_ASSERT (key);
71 if (0 == strcmp ("$jsonSchema", key)) {
72 if (found_jsonschema) {
73 return _mongocrypt_ctx_fail_w_msg (
74 ctx, "duplicate $jsonSchema fields found");
75 }
76 if (!_mongocrypt_buffer_copy_from_document_iter (&ectx->schema,
77 &iter)) {
78 return _mongocrypt_ctx_fail_w_msg (ctx, "malformed $jsonSchema");
79 }
80 found_jsonschema = true;
81 } else {
82 ectx->collinfo_has_siblings = true;
83 }
84 }
85 }
86
87
88 return true;
89 }
90
91 static bool
_mongo_feed_collinfo(mongocrypt_ctx_t * ctx,mongocrypt_binary_t * in)92 _mongo_feed_collinfo (mongocrypt_ctx_t *ctx, mongocrypt_binary_t *in)
93 {
94 bson_t as_bson;
95
96 _mongocrypt_ctx_encrypt_t *ectx;
97
98 ectx = (_mongocrypt_ctx_encrypt_t *) ctx;
99 if (!bson_init_static (&as_bson, in->data, in->len)) {
100 return _mongocrypt_ctx_fail_w_msg (ctx, "BSON malformed");
101 }
102
103 /* Cache the received collinfo. */
104 if (!_mongocrypt_cache_add_copy (
105 &ctx->crypt->cache_collinfo, ectx->ns, &as_bson, ctx->status)) {
106 return _mongocrypt_ctx_fail (ctx);
107 }
108
109 if (!_set_schema_from_collinfo (ctx, &as_bson)) {
110 return false;
111 }
112
113 return true;
114 }
115
116
117 static bool
_mongo_done_collinfo(mongocrypt_ctx_t * ctx)118 _mongo_done_collinfo (mongocrypt_ctx_t *ctx)
119 {
120 _mongocrypt_ctx_encrypt_t *ectx;
121
122 ectx = (_mongocrypt_ctx_encrypt_t *) ctx;
123 ectx->parent.state = MONGOCRYPT_CTX_NEED_MONGO_MARKINGS;
124 return true;
125 }
126
127
128 static bool
_mongo_op_markings(mongocrypt_ctx_t * ctx,mongocrypt_binary_t * out)129 _mongo_op_markings (mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out)
130 {
131 _mongocrypt_ctx_encrypt_t *ectx;
132 bson_t cmd_bson, schema_bson, mongocryptd_cmd_bson;
133
134 ectx = (_mongocrypt_ctx_encrypt_t *) ctx;
135 if (_mongocrypt_buffer_empty (&ectx->mongocryptd_cmd)) {
136 /* first, get the original command. */
137 if (!_mongocrypt_buffer_to_bson (&ectx->original_cmd, &cmd_bson)) {
138 return _mongocrypt_ctx_fail_w_msg (ctx, "invalid BSON cmd");
139 }
140
141 if (_mongocrypt_buffer_empty (&ectx->schema)) {
142 bson_init (&schema_bson);
143 } else if (!_mongocrypt_buffer_to_bson (&ectx->schema, &schema_bson)) {
144 return _mongocrypt_ctx_fail_w_msg (ctx, "invalid BSON schema");
145 }
146
147 bson_copy_to (&cmd_bson, &mongocryptd_cmd_bson);
148 BSON_APPEND_DOCUMENT (&mongocryptd_cmd_bson, "jsonSchema", &schema_bson);
149
150 /* if a local schema was not set, set isRemoteSchema=true */
151 BSON_APPEND_BOOL (
152 &mongocryptd_cmd_bson, "isRemoteSchema", !ectx->used_local_schema);
153 _mongocrypt_buffer_steal_from_bson (&ectx->mongocryptd_cmd,
154 &mongocryptd_cmd_bson);
155
156 bson_destroy (&cmd_bson);
157 bson_destroy (&schema_bson);
158 }
159 out->data = ectx->mongocryptd_cmd.data;
160 out->len = ectx->mongocryptd_cmd.len;
161 return true;
162 }
163
164
165 static bool
_collect_key_from_marking(void * ctx,_mongocrypt_buffer_t * in,mongocrypt_status_t * status)166 _collect_key_from_marking (void *ctx,
167 _mongocrypt_buffer_t *in,
168 mongocrypt_status_t *status)
169 {
170 _mongocrypt_marking_t marking;
171 _mongocrypt_key_broker_t *kb;
172 bool res;
173
174 kb = (_mongocrypt_key_broker_t *) ctx;
175
176 if (!_mongocrypt_marking_parse_unowned (in, &marking, status)) {
177 _mongocrypt_marking_cleanup (&marking);
178 return false;
179 }
180
181 if (marking.has_alt_name) {
182 res = _mongocrypt_key_broker_request_name (kb, &marking.key_alt_name);
183 } else {
184 res = _mongocrypt_key_broker_request_id (kb, &marking.key_id);
185 }
186
187 if (!res) {
188 _mongocrypt_key_broker_status (kb, status);
189 _mongocrypt_marking_cleanup (&marking);
190 return false;
191 }
192
193 _mongocrypt_marking_cleanup (&marking);
194
195 return true;
196 }
197
198
199 static bool
_mongo_feed_markings(mongocrypt_ctx_t * ctx,mongocrypt_binary_t * in)200 _mongo_feed_markings (mongocrypt_ctx_t *ctx, mongocrypt_binary_t *in)
201 {
202 /* Find keys. */
203 bson_t as_bson;
204 bson_iter_t iter;
205 _mongocrypt_ctx_encrypt_t *ectx;
206
207 ectx = (_mongocrypt_ctx_encrypt_t *) ctx;
208 if (!_mongocrypt_binary_to_bson (in, &as_bson)) {
209 return _mongocrypt_ctx_fail_w_msg (ctx, "malformed BSON");
210 }
211
212 if (bson_iter_init_find (&iter, &as_bson, "schemaRequiresEncryption") &&
213 !bson_iter_as_bool (&iter)) {
214 /* TODO: update cache: this schema does not require encryption. */
215
216 /* If using a local schema, warn if there are no encrypted fields. */
217 if (ectx->used_local_schema) {
218 _mongocrypt_log (
219 &ctx->crypt->log,
220 MONGOCRYPT_LOG_LEVEL_WARNING,
221 "local schema used but does not have encryption specifiers");
222 }
223 return true;
224 } else {
225 /* if the schema requires encryption, but has sibling validators, error.
226 */
227 if (ectx->collinfo_has_siblings) {
228 return _mongocrypt_ctx_fail_w_msg (ctx,
229 "schema requires encryption, "
230 "but collection JSON schema "
231 "validator has siblings");
232 }
233 }
234
235 if (bson_iter_init_find (&iter, &as_bson, "hasEncryptedPlaceholders") &&
236 !bson_iter_as_bool (&iter)) {
237 return true;
238 }
239
240 if (!bson_iter_init_find (&iter, &as_bson, "result")) {
241 return _mongocrypt_ctx_fail_w_msg (ctx, "malformed marking, no 'result'");
242 }
243
244 if (!_mongocrypt_buffer_copy_from_document_iter (&ectx->marked_cmd, &iter)) {
245 return _mongocrypt_ctx_fail_w_msg (
246 ctx, "malformed marking, 'result' must be a document");
247 }
248
249 if (!bson_iter_recurse (&iter, &iter)) {
250 return _mongocrypt_ctx_fail_w_msg (
251 ctx, "malformed marking, could not recurse into 'result'");
252 }
253 if (!_mongocrypt_traverse_binary_in_bson (_collect_key_from_marking,
254 (void *) &ctx->kb,
255 TRAVERSE_MATCH_MARKING,
256 &iter,
257 ctx->status)) {
258 return _mongocrypt_ctx_fail (ctx);
259 }
260
261 return true;
262 }
263
264
265 static bool
_mongo_done_markings(mongocrypt_ctx_t * ctx)266 _mongo_done_markings (mongocrypt_ctx_t *ctx)
267 {
268 (void) _mongocrypt_key_broker_requests_done (&ctx->kb);
269 return _mongocrypt_ctx_state_from_key_broker (ctx);
270 }
271
272
273 static bool
_marking_to_bson_value(void * ctx,_mongocrypt_marking_t * marking,bson_value_t * out,mongocrypt_status_t * status)274 _marking_to_bson_value (void *ctx,
275 _mongocrypt_marking_t *marking,
276 bson_value_t *out,
277 mongocrypt_status_t *status)
278 {
279 _mongocrypt_ciphertext_t ciphertext;
280 _mongocrypt_buffer_t serialized_ciphertext = {0};
281 bool ret = false;
282
283 BSON_ASSERT (out);
284
285 _mongocrypt_ciphertext_init (&ciphertext);
286
287 if (!_mongocrypt_marking_to_ciphertext (ctx, marking, &ciphertext, status)) {
288 goto fail;
289 }
290
291 if (!_mongocrypt_serialize_ciphertext (&ciphertext,
292 &serialized_ciphertext)) {
293 CLIENT_ERR ("malformed ciphertext");
294 goto fail;
295 };
296
297 /* ownership of serialized_ciphertext is transferred to caller. */
298 out->value_type = BSON_TYPE_BINARY;
299 out->value.v_binary.data = serialized_ciphertext.data;
300 out->value.v_binary.data_len = serialized_ciphertext.len;
301 out->value.v_binary.subtype = (bson_subtype_t) 6;
302
303 ret = true;
304
305 fail:
306 _mongocrypt_ciphertext_cleanup (&ciphertext);
307 return ret;
308 }
309
310
311 static bool
_replace_marking_with_ciphertext(void * ctx,_mongocrypt_buffer_t * in,bson_value_t * out,mongocrypt_status_t * status)312 _replace_marking_with_ciphertext (void *ctx,
313 _mongocrypt_buffer_t *in,
314 bson_value_t *out,
315 mongocrypt_status_t *status)
316 {
317 _mongocrypt_marking_t marking;
318 bool ret;
319
320 BSON_ASSERT (in);
321
322 memset (&marking, 0, sizeof (marking));
323
324 if (!_mongocrypt_marking_parse_unowned (in, &marking, status)) {
325 _mongocrypt_marking_cleanup (&marking);
326 return false;
327 }
328
329 ret = _marking_to_bson_value (ctx, &marking, out, status);
330 _mongocrypt_marking_cleanup (&marking);
331 return ret;
332 }
333
334 static bool
_finalize(mongocrypt_ctx_t * ctx,mongocrypt_binary_t * out)335 _finalize (mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out)
336 {
337 bson_t as_bson, converted;
338 bson_iter_t iter;
339 _mongocrypt_ctx_encrypt_t *ectx;
340 bool res;
341
342 ectx = (_mongocrypt_ctx_encrypt_t *) ctx;
343
344 if (!ectx->explicit) {
345 if (ctx->nothing_to_do) {
346 _mongocrypt_buffer_to_binary (&ectx->original_cmd, out);
347 ctx->state = MONGOCRYPT_CTX_DONE;
348 return true;
349 }
350 if (!_mongocrypt_buffer_to_bson (&ectx->marked_cmd, &as_bson)) {
351 return _mongocrypt_ctx_fail_w_msg (ctx, "malformed bson");
352 }
353
354 bson_iter_init (&iter, &as_bson);
355 bson_init (&converted);
356 if (!_mongocrypt_transform_binary_in_bson (
357 _replace_marking_with_ciphertext,
358 &ctx->kb,
359 TRAVERSE_MATCH_MARKING,
360 &iter,
361 &converted,
362 ctx->status)) {
363 return _mongocrypt_ctx_fail (ctx);
364 }
365 } else {
366 /* For explicit encryption, we have no marking, but we can fake one */
367 _mongocrypt_marking_t marking;
368 bson_value_t value;
369
370 memset (&value, 0, sizeof (value));
371
372 _mongocrypt_marking_init (&marking);
373
374 if (!_mongocrypt_buffer_to_bson (&ectx->original_cmd, &as_bson)) {
375 return _mongocrypt_ctx_fail_w_msg (ctx, "malformed bson");
376 }
377
378 if (!bson_iter_init_find (&iter, &as_bson, "v")) {
379 return _mongocrypt_ctx_fail_w_msg (ctx,
380 "invalid msg, must contain 'v'");
381 }
382
383
384 memcpy (&marking.v_iter, &iter, sizeof (bson_iter_t));
385 marking.algorithm = ctx->opts.algorithm;
386 _mongocrypt_buffer_set_to (&ctx->opts.key_id, &marking.key_id);
387 if (ctx->opts.key_alt_names) {
388 bson_value_copy (&ctx->opts.key_alt_names->value,
389 &marking.key_alt_name);
390 marking.has_alt_name = true;
391 }
392
393 bson_init (&converted);
394 res = _marking_to_bson_value (&ctx->kb, &marking, &value, ctx->status);
395 if (res) {
396 bson_append_value (&converted, MONGOCRYPT_STR_AND_LEN ("v"), &value);
397 }
398
399 bson_value_destroy (&value);
400 _mongocrypt_marking_cleanup (&marking);
401
402 if (!res) {
403 bson_destroy (&converted);
404 return _mongocrypt_ctx_fail (ctx);
405 }
406 }
407
408 _mongocrypt_buffer_steal_from_bson (&ectx->encrypted_cmd, &converted);
409 _mongocrypt_buffer_to_binary (&ectx->encrypted_cmd, out);
410 ctx->state = MONGOCRYPT_CTX_DONE;
411
412 return true;
413 }
414
415
416 static void
_cleanup(mongocrypt_ctx_t * ctx)417 _cleanup (mongocrypt_ctx_t *ctx)
418 {
419 _mongocrypt_ctx_encrypt_t *ectx;
420
421 ectx = (_mongocrypt_ctx_encrypt_t *) ctx;
422 bson_free (ectx->ns);
423 bson_free (ectx->db_name);
424 bson_free (ectx->coll_name);
425 _mongocrypt_buffer_cleanup (&ectx->list_collections_filter);
426 _mongocrypt_buffer_cleanup (&ectx->schema);
427 _mongocrypt_buffer_cleanup (&ectx->original_cmd);
428 _mongocrypt_buffer_cleanup (&ectx->mongocryptd_cmd);
429 _mongocrypt_buffer_cleanup (&ectx->marked_cmd);
430 _mongocrypt_buffer_cleanup (&ectx->encrypted_cmd);
431 }
432
433
434 static bool
_try_schema_from_schema_map(mongocrypt_ctx_t * ctx)435 _try_schema_from_schema_map (mongocrypt_ctx_t *ctx)
436 {
437 mongocrypt_t *crypt;
438 _mongocrypt_ctx_encrypt_t *ectx;
439 bson_t schema_map;
440 bson_iter_t iter;
441
442 crypt = ctx->crypt;
443 ectx = (_mongocrypt_ctx_encrypt_t *) ctx;
444
445 if (_mongocrypt_buffer_empty (&crypt->opts.schema_map)) {
446 /* No schema map set. */
447 return true;
448 }
449
450 if (!_mongocrypt_buffer_to_bson (&crypt->opts.schema_map, &schema_map)) {
451 return _mongocrypt_ctx_fail_w_msg (ctx, "malformed schema map");
452 }
453
454 if (bson_iter_init_find (&iter, &schema_map, ectx->ns)) {
455 if (!_mongocrypt_buffer_copy_from_document_iter (&ectx->schema, &iter)) {
456 return _mongocrypt_ctx_fail_w_msg (ctx, "malformed schema map");
457 }
458 ectx->used_local_schema = true;
459 ctx->state = MONGOCRYPT_CTX_NEED_MONGO_MARKINGS;
460 }
461
462 /* No schema found in map. */
463 return true;
464 }
465
466
467 static bool
_try_schema_from_cache(mongocrypt_ctx_t * ctx)468 _try_schema_from_cache (mongocrypt_ctx_t *ctx)
469 {
470 _mongocrypt_ctx_encrypt_t *ectx;
471 bson_t *collinfo = NULL;
472
473 ectx = (_mongocrypt_ctx_encrypt_t *) ctx;
474
475 /* Otherwise, we need a remote schema. Check if we have a response to
476 * listCollections cached. */
477 if (!_mongocrypt_cache_get (&ctx->crypt->cache_collinfo,
478 ectx->ns /* null terminated */,
479 (void **) &collinfo)) {
480 return _mongocrypt_ctx_fail_w_msg (ctx, "failed to retrieve from cache");
481 }
482
483 if (collinfo) {
484 if (!_set_schema_from_collinfo (ctx, collinfo)) {
485 return _mongocrypt_ctx_fail (ctx);
486 }
487 ctx->state = MONGOCRYPT_CTX_NEED_MONGO_MARKINGS;
488 } else {
489 /* we need to get it. */
490 ctx->state = MONGOCRYPT_CTX_NEED_MONGO_COLLINFO;
491 }
492
493 bson_destroy (collinfo);
494 return true;
495 }
496
497 static bool
_permitted_for_encryption(bson_iter_t * iter,mongocrypt_encryption_algorithm_t algo,mongocrypt_status_t * status)498 _permitted_for_encryption (bson_iter_t *iter,
499 mongocrypt_encryption_algorithm_t algo,
500 mongocrypt_status_t *status)
501 {
502 bson_type_t bson_type;
503 const bson_value_t *bson_value = bson_iter_value (iter);
504 bool ret = false;
505
506 if (!bson_value) {
507 CLIENT_ERR ("Unknown BSON type");
508 goto fail;
509 }
510 bson_type = bson_value->value_type;
511 switch (bson_type) {
512 case BSON_TYPE_NULL:
513 case BSON_TYPE_MINKEY:
514 case BSON_TYPE_MAXKEY:
515 case BSON_TYPE_UNDEFINED:
516 CLIENT_ERR ("BSON type invalid for encryption");
517 goto fail;
518 case BSON_TYPE_BINARY:
519 if (bson_value->value.v_binary.subtype == 6) {
520 CLIENT_ERR ("BSON binary subtype 6 is invalid for encryption");
521 goto fail;
522 }
523 /* ok */
524 break;
525 case BSON_TYPE_DOUBLE:
526 case BSON_TYPE_DOCUMENT:
527 case BSON_TYPE_ARRAY:
528 case BSON_TYPE_CODEWSCOPE:
529 case BSON_TYPE_BOOL:
530 case BSON_TYPE_DECIMAL128:
531 if (algo == MONGOCRYPT_ENCRYPTION_ALGORITHM_DETERMINISTIC) {
532 CLIENT_ERR ("BSON type invalid for deterministic encryption");
533 goto fail;
534 }
535 break;
536 case BSON_TYPE_UTF8:
537 case BSON_TYPE_OID:
538 case BSON_TYPE_DATE_TIME:
539 case BSON_TYPE_REGEX:
540 case BSON_TYPE_DBPOINTER:
541 case BSON_TYPE_CODE:
542 case BSON_TYPE_SYMBOL:
543 case BSON_TYPE_INT32:
544 case BSON_TYPE_TIMESTAMP:
545 case BSON_TYPE_INT64:
546 /* ok */
547 break;
548 case BSON_TYPE_EOD:
549 default:
550 CLIENT_ERR ("invalid BSON value type 00");
551 goto fail;
552 }
553
554 ret = true;
555 fail:
556 return ret;
557 }
558
559 bool
mongocrypt_ctx_explicit_encrypt_init(mongocrypt_ctx_t * ctx,mongocrypt_binary_t * msg)560 mongocrypt_ctx_explicit_encrypt_init (mongocrypt_ctx_t *ctx,
561 mongocrypt_binary_t *msg)
562 {
563 _mongocrypt_ctx_encrypt_t *ectx;
564 bson_t as_bson;
565 bson_iter_t iter;
566 _mongocrypt_ctx_opts_spec_t opts_spec;
567
568 if (!ctx) {
569 return false;
570 }
571 memset (&opts_spec, 0, sizeof (opts_spec));
572 opts_spec.key_descriptor = OPT_REQUIRED;
573 opts_spec.algorithm = OPT_REQUIRED;
574
575 if (!_mongocrypt_ctx_init (ctx, &opts_spec)) {
576 return false;
577 }
578
579 ectx = (_mongocrypt_ctx_encrypt_t *) ctx;
580 ctx->type = _MONGOCRYPT_TYPE_ENCRYPT;
581 ectx->explicit = true;
582 ctx->vtable.finalize = _finalize;
583 ctx->vtable.cleanup = _cleanup;
584
585 if (!msg || !msg->data) {
586 return _mongocrypt_ctx_fail_w_msg (
587 ctx, "msg required for explicit encryption");
588 }
589
590 if (ctx->opts.key_alt_names) {
591 if (!_mongocrypt_key_broker_request_name (
592 &ctx->kb, &ctx->opts.key_alt_names->value)) {
593 return _mongocrypt_ctx_fail (ctx);
594 }
595 } else {
596 if (!_mongocrypt_key_broker_request_id (&ctx->kb, &ctx->opts.key_id)) {
597 return _mongocrypt_ctx_fail (ctx);
598 }
599 }
600
601 _mongocrypt_buffer_init (&ectx->original_cmd);
602
603 _mongocrypt_buffer_copy_from_binary (&ectx->original_cmd, msg);
604 if (!_mongocrypt_buffer_to_bson (&ectx->original_cmd, &as_bson)) {
605 return _mongocrypt_ctx_fail_w_msg (ctx, "msg must be bson");
606 }
607
608 if (ctx->crypt->log.trace_enabled) {
609 char *cmd_val;
610 cmd_val = _mongocrypt_new_json_string_from_binary (msg);
611 _mongocrypt_log (&ctx->crypt->log,
612 MONGOCRYPT_LOG_LEVEL_TRACE,
613 "%s (%s=\"%s\")",
614 BSON_FUNC,
615 "msg",
616 cmd_val);
617 bson_free (cmd_val);
618 }
619
620 if (!bson_iter_init_find (&iter, &as_bson, "v")) {
621 return _mongocrypt_ctx_fail_w_msg (ctx, "invalid msg, must contain 'v'");
622 }
623
624 if (!_permitted_for_encryption (&iter, ctx->opts.algorithm, ctx->status)) {
625 return _mongocrypt_ctx_fail (ctx);
626 }
627
628 (void) _mongocrypt_key_broker_requests_done (&ctx->kb);
629 return _mongocrypt_ctx_state_from_key_broker (ctx);
630 }
631
632 static bool
_check_cmd_for_auto_encrypt(mongocrypt_binary_t * cmd,bool * bypass,char ** collname,mongocrypt_status_t * status)633 _check_cmd_for_auto_encrypt (mongocrypt_binary_t *cmd,
634 bool *bypass,
635 char **collname,
636 mongocrypt_status_t *status)
637 {
638 bson_t as_bson;
639 bson_iter_t iter, ns_iter;
640 const char *cmd_name;
641 bool eligible = false;
642
643 *bypass = false;
644
645 if (!_mongocrypt_binary_to_bson (cmd, &as_bson) ||
646 !bson_iter_init (&iter, &as_bson)) {
647 CLIENT_ERR ("invalid BSON");
648 return false;
649 }
650
651 /* The command name is the first key. */
652 if (!bson_iter_next (&iter)) {
653 CLIENT_ERR ("invalid empty BSON");
654 return false;
655 }
656
657 cmd_name = bson_iter_key (&iter);
658 BSON_ASSERT (cmd_name);
659
660 /* get the collection name (or NULL if database/client command). */
661 if (0 == strcmp (cmd_name, "explain")) {
662 if (!BSON_ITER_HOLDS_DOCUMENT (&iter)) {
663 CLIENT_ERR ("explain value is not a document");
664 return false;
665 }
666 if (!bson_iter_recurse (&iter, &ns_iter)) {
667 CLIENT_ERR ("malformed BSON for encrypt command");
668 return false;
669 }
670 if (!bson_iter_next (&ns_iter)) {
671 CLIENT_ERR ("invalid empty BSON");
672 return false;
673 }
674 } else {
675 memcpy (&ns_iter, &iter, sizeof (iter));
676 }
677
678 if (BSON_ITER_HOLDS_UTF8 (&ns_iter)) {
679 *collname = bson_strdup (bson_iter_utf8 (&ns_iter, NULL));
680 } else {
681 *collname = NULL;
682 }
683
684 /* check if command is eligible for auto encryption, bypassed, or ineligible.
685 */
686 if (0 == strcmp (cmd_name, "aggregate")) {
687 /* collection level aggregate ok, database/client is not. */
688 eligible = true;
689 } else if (0 == strcmp (cmd_name, "count")) {
690 eligible = true;
691 } else if (0 == strcmp (cmd_name, "distinct")) {
692 eligible = true;
693 } else if (0 == strcmp (cmd_name, "delete")) {
694 eligible = true;
695 } else if (0 == strcmp (cmd_name, "find")) {
696 eligible = true;
697 } else if (0 == strcmp (cmd_name, "findAndModify")) {
698 eligible = true;
699 } else if (0 == strcmp (cmd_name, "getMore")) {
700 *bypass = true;
701 } else if (0 == strcmp (cmd_name, "insert")) {
702 eligible = true;
703 } else if (0 == strcmp (cmd_name, "update")) {
704 eligible = true;
705 } else if (0 == strcmp (cmd_name, "authenticate")) {
706 *bypass = true;
707 } else if (0 == strcmp (cmd_name, "getnonce")) {
708 *bypass = true;
709 } else if (0 == strcmp (cmd_name, "logout")) {
710 *bypass = true;
711 } else if (0 == bson_strcasecmp (cmd_name, "isMaster")) {
712 /* use case insensitive compare for ismaster, since some drivers send
713 * "ismaster" and others send "isMaster" */
714 *bypass = true;
715 } else if (0 == strcmp (cmd_name, "abortTransaction")) {
716 *bypass = true;
717 } else if (0 == strcmp (cmd_name, "commitTransaction")) {
718 *bypass = true;
719 } else if (0 == strcmp (cmd_name, "endSessions")) {
720 *bypass = true;
721 } else if (0 == strcmp (cmd_name, "startSession")) {
722 *bypass = true;
723 } else if (0 == strcmp (cmd_name, "create")) {
724 *bypass = true;
725 } else if (0 == strcmp (cmd_name, "createIndexes")) {
726 *bypass = true;
727 } else if (0 == strcmp (cmd_name, "drop")) {
728 *bypass = true;
729 } else if (0 == strcmp (cmd_name, "dropDatabase")) {
730 *bypass = true;
731 } else if (0 == strcmp (cmd_name, "dropIndexes")) {
732 *bypass = true;
733 } else if (0 == strcmp (cmd_name, "killCursors")) {
734 *bypass = true;
735 } else if (0 == strcmp (cmd_name, "listCollections")) {
736 *bypass = true;
737 } else if (0 == strcmp (cmd_name, "listDatabases")) {
738 *bypass = true;
739 } else if (0 == strcmp (cmd_name, "listIndexes")) {
740 *bypass = true;
741 } else if (0 == strcmp (cmd_name, "renameCollection")) {
742 *bypass = true;
743 } else if (0 == strcmp (cmd_name, "explain")) {
744 eligible = true;
745 } else if (0 == strcmp (cmd_name, "ping")) {
746 *bypass = true;
747 } else if (0 == strcmp (cmd_name, "saslStart")) {
748 *bypass = true;
749 } else if (0 == strcmp (cmd_name, "saslContinue")) {
750 *bypass = true;
751 } else if (0 == strcmp (cmd_name, "killAllSessions")) {
752 *bypass = true;
753 } else if (0 == strcmp (cmd_name, "killSessions")) {
754 *bypass = true;
755 } else if (0 == strcmp (cmd_name, "killAllSessionsByPattern")) {
756 *bypass = true;
757 } else if (0 == strcmp (cmd_name, "refreshSessions")) {
758 *bypass = true;
759 }
760
761 /* database/client commands are ineligible. */
762 if (eligible) {
763 if (!*collname) {
764 CLIENT_ERR (
765 "non-collection command not supported for auto encryption: %s",
766 cmd_name);
767 return false;
768 }
769 if (0 == strlen (*collname)) {
770 CLIENT_ERR ("empty collection name on command: %s", cmd_name);
771 return false;
772 }
773 }
774
775 if (eligible || *bypass) {
776 return true;
777 }
778
779 CLIENT_ERR ("command not supported for auto encryption: %s", cmd_name);
780 return false;
781 }
782
783 bool
mongocrypt_ctx_encrypt_init(mongocrypt_ctx_t * ctx,const char * db,int32_t db_len,mongocrypt_binary_t * cmd)784 mongocrypt_ctx_encrypt_init (mongocrypt_ctx_t *ctx,
785 const char *db,
786 int32_t db_len,
787 mongocrypt_binary_t *cmd)
788 {
789 _mongocrypt_ctx_encrypt_t *ectx;
790 _mongocrypt_ctx_opts_spec_t opts_spec;
791 bool bypass;
792
793 if (!ctx) {
794 return false;
795 }
796 memset (&opts_spec, 0, sizeof (opts_spec));
797 opts_spec.schema = OPT_OPTIONAL;
798 if (!_mongocrypt_ctx_init (ctx, &opts_spec)) {
799 return false;
800 }
801
802 ectx = (_mongocrypt_ctx_encrypt_t *) ctx;
803 ctx->type = _MONGOCRYPT_TYPE_ENCRYPT;
804 ectx->explicit = false;
805 ctx->vtable.mongo_op_collinfo = _mongo_op_collinfo;
806 ctx->vtable.mongo_feed_collinfo = _mongo_feed_collinfo;
807 ctx->vtable.mongo_done_collinfo = _mongo_done_collinfo;
808 ctx->vtable.mongo_op_collinfo = _mongo_op_collinfo;
809 ctx->vtable.mongo_op_markings = _mongo_op_markings;
810 ctx->vtable.mongo_feed_markings = _mongo_feed_markings;
811 ctx->vtable.mongo_done_markings = _mongo_done_markings;
812 ctx->vtable.finalize = _finalize;
813 ctx->vtable.cleanup = _cleanup;
814 ctx->vtable.mongo_op_collinfo = _mongo_op_collinfo;
815 ctx->vtable.mongo_feed_collinfo = _mongo_feed_collinfo;
816 ctx->vtable.mongo_done_collinfo = _mongo_done_collinfo;
817
818
819 if (!cmd || !cmd->data) {
820 return _mongocrypt_ctx_fail_w_msg (ctx, "invalid command");
821 }
822
823 _mongocrypt_buffer_copy_from_binary (&ectx->original_cmd, cmd);
824
825 if (!_check_cmd_for_auto_encrypt (
826 cmd, &bypass, &ectx->coll_name, ctx->status)) {
827 return _mongocrypt_ctx_fail (ctx);
828 }
829
830 if (bypass) {
831 ctx->nothing_to_do = true;
832 ctx->state = MONGOCRYPT_CTX_READY;
833 return true;
834 }
835
836 /* if _check_cmd_for_auto_encrypt did not bypass or error, a collection name
837 * must have been set. */
838 if (!ectx->coll_name) {
839 return _mongocrypt_ctx_fail_w_msg (
840 ctx,
841 "unexpected error: did not bypass or error but no collection name");
842 }
843
844 if (!_mongocrypt_validate_and_copy_string (db, db_len, &ectx->db_name) ||
845 0 == strlen (ectx->db_name)) {
846 return _mongocrypt_ctx_fail_w_msg (ctx, "invalid db");
847 }
848
849 ectx->ns = bson_strdup_printf ("%s.%s", ectx->db_name, ectx->coll_name);
850
851 if (ctx->opts.masterkey_aws_region || ctx->opts.masterkey_aws_cmk) {
852 return _mongocrypt_ctx_fail_w_msg (
853 ctx, "aws masterkey options must not be set");
854 }
855
856 if (!_mongocrypt_buffer_empty (&ctx->opts.key_id)) {
857 return _mongocrypt_ctx_fail_w_msg (
858 ctx, "key_id must not be set for auto encryption");
859 }
860
861 if (ctx->opts.algorithm != MONGOCRYPT_ENCRYPTION_ALGORITHM_NONE) {
862 return _mongocrypt_ctx_fail_w_msg (
863 ctx, "algorithm must not be set for auto encryption");
864 }
865
866 if (ctx->crypt->log.trace_enabled) {
867 char *cmd_val;
868 cmd_val = _mongocrypt_new_json_string_from_binary (cmd);
869 _mongocrypt_log (&ctx->crypt->log,
870 MONGOCRYPT_LOG_LEVEL_TRACE,
871 "%s (%s=\"%s\", %s=%d, %s=\"%s\")",
872 BSON_FUNC,
873 "db",
874 ectx->db_name,
875 "db_len",
876 db_len,
877 "cmd",
878 cmd_val);
879 bson_free (cmd_val);
880 }
881
882 /* Check if we have a local schema from schema_map */
883 if (!_try_schema_from_schema_map (ctx)) {
884 return false;
885 }
886
887 /* If we didn't have a local schema, try the cache. */
888 if (_mongocrypt_buffer_empty (&ectx->schema)) {
889 if (!_try_schema_from_cache (ctx)) {
890 return false;
891 }
892 }
893
894 /* Otherwise, we need the the driver to fetch the schema. */
895 if (_mongocrypt_buffer_empty (&ectx->schema)) {
896 ctx->state = MONGOCRYPT_CTX_NEED_MONGO_COLLINFO;
897 }
898 return true;
899 }
900