22
22
#include "mongocrypt-ctx-private.h"
23
23
#include "mongocrypt-key-broker-private.h"
24
24
#include "mongocrypt-marking-private.h"
25
+ #include "mongocrypt-private.h"
25
26
#include "mongocrypt-traverse-util-private.h"
26
27
#include "mongocrypt-util-private.h" // mc_iter_document_as_bson
27
28
#include "mongocrypt.h"
28
29
29
30
/* _fle2_append_encryptedFieldConfig copies encryptedFieldConfig and applies
30
- * default state collection names for escCollection, and ecocCollection if required. */
31
+ * default state collection names for escCollection and ecocCollection, and default strEncodeVersion, if required. */
31
32
static bool _fle2_append_encryptedFieldConfig (const mongocrypt_ctx_t * ctx ,
32
33
bson_t * dst ,
33
34
bson_t * encryptedFieldConfig ,
@@ -36,7 +37,9 @@ static bool _fle2_append_encryptedFieldConfig(const mongocrypt_ctx_t *ctx,
36
37
bson_iter_t iter ;
37
38
bool has_escCollection = false;
38
39
bool has_ecocCollection = false;
40
+ bool has_strEncodeVersion = false;
39
41
42
+ BSON_ASSERT_PARAM (ctx );
40
43
BSON_ASSERT_PARAM (dst );
41
44
BSON_ASSERT_PARAM (encryptedFieldConfig );
42
45
BSON_ASSERT_PARAM (target_coll );
@@ -53,6 +56,9 @@ static bool _fle2_append_encryptedFieldConfig(const mongocrypt_ctx_t *ctx,
53
56
if (strcmp (bson_iter_key (& iter ), "ecocCollection" ) == 0 ) {
54
57
has_ecocCollection = true;
55
58
}
59
+ if (strcmp (bson_iter_key (& iter ), "strEncodeVersion" ) == 0 ) {
60
+ has_strEncodeVersion = true;
61
+ }
56
62
if (!BSON_APPEND_VALUE (dst , bson_iter_key (& iter ), bson_iter_value (& iter ))) {
57
63
CLIENT_ERR ("unable to append field: %s" , bson_iter_key (& iter ));
58
64
return false;
@@ -77,6 +83,18 @@ static bool _fle2_append_encryptedFieldConfig(const mongocrypt_ctx_t *ctx,
77
83
}
78
84
bson_free (default_ecocCollection );
79
85
}
86
+ if (!has_strEncodeVersion ) {
87
+ _mongocrypt_ctx_encrypt_t * ectx = (_mongocrypt_ctx_encrypt_t * )ctx ;
88
+ // Check str_encode_version on the EncryptedFieldConfig object to see whether we should append or not. 0
89
+ // indicates that there was no text search query in the EFC and the strEncodeVersion was not set on the EFC; in
90
+ // this case, we should not append strEncodeVersion, as mongocryptd/mongod may not understand it.
91
+ if (ectx -> efc .str_encode_version != 0 ) {
92
+ if (!BSON_APPEND_INT32 (dst , "strEncodeVersion" , (int32_t )ectx -> efc .str_encode_version )) {
93
+ CLIENT_ERR ("unable to append strEncodeVersion" );
94
+ return false;
95
+ }
96
+ }
97
+ }
80
98
return true;
81
99
}
82
100
@@ -1433,6 +1451,100 @@ _fle2_strip_encryptionInformation(const char *cmd_name, bson_t *cmd /* in and ou
1433
1451
return ok ;
1434
1452
}
1435
1453
1454
+ /*
1455
+ * Checks the "encryptedFields.strEncodeVersion" field for "create" commands for validity, and sets it to the default if
1456
+ * it does not exist.
1457
+ */
1458
+ static bool _fle2_fixup_encryptedFields_strEncodeVersion (const char * cmd_name ,
1459
+ bson_t * cmd /* in and out */ ,
1460
+ const mc_EncryptedFieldConfig_t * efc ,
1461
+ mongocrypt_status_t * status ) {
1462
+ BSON_ASSERT_PARAM (cmd_name );
1463
+ BSON_ASSERT_PARAM (cmd );
1464
+ BSON_ASSERT_PARAM (efc );
1465
+
1466
+ if (0 == strcmp (cmd_name , "create" )) {
1467
+ bson_iter_t ef_iter ;
1468
+ if (!bson_iter_init_find (& ef_iter , cmd , "encryptedFields" )) {
1469
+ // No encryptedFields, nothing to check or fix
1470
+ return true;
1471
+ }
1472
+ if (!BSON_ITER_HOLDS_DOCUMENT (& ef_iter )) {
1473
+ CLIENT_ERR ("_fle2_fixup_encryptedFields_strEncodeVersion: Expected encryptedFields to be type obj, got: %s" ,
1474
+ mc_bson_type_to_string (bson_iter_type (& ef_iter )));
1475
+ return false;
1476
+ }
1477
+ bson_iter_t sev_iter ;
1478
+ if (!bson_iter_recurse (& ef_iter , & sev_iter )) {
1479
+ CLIENT_ERR ("_fle2_fixup_encryptedFields_strEncodeVersion: Failed to recurse bson_iter" );
1480
+ return false;
1481
+ }
1482
+ if (!bson_iter_find (& sev_iter , "strEncodeVersion" )) {
1483
+ if (efc -> str_encode_version == 0 ) {
1484
+ // Unset StrEncodeVersion matches the EFC, nothing to fix.
1485
+ return true;
1486
+ }
1487
+
1488
+ // No strEncodeVersion and the EFC has a nonzero strEncodeVersion, add it.
1489
+ // Initialize the new cmd object from the old one, excluding encryptedFields.
1490
+ bson_t fixed = BSON_INITIALIZER ;
1491
+ bson_copy_to_excluding_noinit (cmd , & fixed , "encryptedFields" , NULL );
1492
+
1493
+ // Recurse the original encryptedFields and copy everything over.
1494
+ bson_iter_t copy_iter ;
1495
+ if (!bson_iter_recurse (& ef_iter , & copy_iter )) {
1496
+ CLIENT_ERR ("_fle2_fixup_encryptedFields_strEncodeVersion: Failed to recurse bson_iter" );
1497
+ goto fail ;
1498
+ }
1499
+ bson_t fixed_ef ;
1500
+ if (!BSON_APPEND_DOCUMENT_BEGIN (& fixed , "encryptedFields" , & fixed_ef )) {
1501
+ CLIENT_ERR ("_fle2_fixup_encryptedFields_strEncodeVersion: Failed to start appending encryptedFields" );
1502
+ goto fail ;
1503
+ }
1504
+ while (bson_iter_next (& copy_iter )) {
1505
+ if (!bson_append_iter (& fixed_ef , NULL , 0 , & copy_iter )) {
1506
+ CLIENT_ERR ("_fle2_fixup_encryptedFields_strEncodeVersion: Failed to copy element" );
1507
+ goto fail ;
1508
+ }
1509
+ }
1510
+
1511
+ // Add the EFC's strEncodeVersion to encryptedFields.
1512
+ if (!BSON_APPEND_INT32 (& fixed_ef , "strEncodeVersion" , efc -> str_encode_version )) {
1513
+ CLIENT_ERR ("_fle2_fixup_encryptedFields_strEncodeVersion: Failed to append strEncodeVersion" );
1514
+ goto fail ;
1515
+ }
1516
+ if (!bson_append_document_end (& fixed , & fixed_ef )) {
1517
+ CLIENT_ERR ("_fle2_fixup_encryptedFields_strEncodeVersion: Failed to finish appending encryptedFields" );
1518
+ goto fail ;
1519
+ }
1520
+
1521
+ bson_destroy (cmd );
1522
+ if (!bson_steal (cmd , & fixed )) {
1523
+ CLIENT_ERR ("_fle2_fixup_encryptedFields_strEncodeVersion: Failed to steal BSON" );
1524
+ goto fail ;
1525
+ }
1526
+ return true;
1527
+ fail :
1528
+ bson_destroy (& fixed );
1529
+ return false;
1530
+ } else {
1531
+ // Check strEncodeVersion for match against EFC
1532
+ if (!BSON_ITER_HOLDS_INT32 (& sev_iter )) {
1533
+ CLIENT_ERR ("expected 'strEncodeVersion' to be type int32, got: %d" , bson_iter_type (& sev_iter ));
1534
+ return false;
1535
+ }
1536
+ int32_t version = bson_iter_int32 (& sev_iter );
1537
+ if (version != efc -> str_encode_version ) {
1538
+ CLIENT_ERR ("'strEncodeVersion' of %d does not match efc->str_encode_version of %d" ,
1539
+ version ,
1540
+ efc -> str_encode_version );
1541
+ return false;
1542
+ }
1543
+ }
1544
+ }
1545
+ return true;
1546
+ }
1547
+
1436
1548
/* Process a call to mongocrypt_ctx_finalize when an encryptedFieldConfig is
1437
1549
* associated with the command. */
1438
1550
static bool _fle2_finalize (mongocrypt_ctx_t * ctx , mongocrypt_binary_t * out ) {
@@ -1505,6 +1617,13 @@ static bool _fle2_finalize(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
1505
1617
return _mongocrypt_ctx_fail (ctx );
1506
1618
}
1507
1619
1620
+ /* If this is a create command, append the encryptedFields.strEncodeVersion field if it's necessary. If the field
1621
+ * already exists, check it against the EFC for correctness. */
1622
+ if (!_fle2_fixup_encryptedFields_strEncodeVersion (command_name , & converted , & ectx -> efc , ctx -> status )) {
1623
+ bson_destroy (& converted );
1624
+ return _mongocrypt_ctx_fail (ctx );
1625
+ }
1626
+
1508
1627
/* Append a new 'encryptionInformation'. */
1509
1628
if (!result .must_omit && !ectx -> used_empty_encryptedFields ) {
1510
1629
if (!_fle2_insert_encryptionInformation (ctx ,
0 commit comments