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