Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/mc-efc-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ typedef enum _supported_query_type_flags {
typedef struct _mc_EncryptedField_t {
supported_query_type_flags supported_queries;
_mongocrypt_buffer_t keyId;
const char *keyAltName;
const char *path;
struct _mc_EncryptedField_t *next;
} mc_EncryptedField_t;
Expand Down
49 changes: 41 additions & 8 deletions src/mc-efc.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,18 +85,46 @@ static bool _parse_field(mc_EncryptedFieldConfig_t *efc, bson_t *field, mongocry
BSON_ASSERT_PARAM(efc);
BSON_ASSERT_PARAM(field);

if (!bson_iter_init_find(&field_iter, field, "keyId")) {
CLIENT_ERR("unable to find 'keyId' in 'field' document");
bool has_keyid = false;
bool has_keyaltname = false;
if (bson_iter_init_find(&field_iter, field, "keyId")) {
has_keyid = true;
}
if (bson_iter_init_find(&field_iter, field, "keyAltName")) {
has_keyaltname = true;
}
if (!(has_keyid || has_keyaltname)) {
CLIENT_ERR("unable to find 'keyId' or 'keyAltName' in 'field' document");
return false;
}
if (!BSON_ITER_HOLDS_BINARY(&field_iter)) {
CLIENT_ERR("expected 'fields.keyId' to be type binary, got: %d", (int)bson_iter_type(&field_iter));
if (has_keyid && has_keyaltname) {
CLIENT_ERR("only one of 'keyId' or 'keyAltName may be in 'field' document");
return false;
}

_mongocrypt_buffer_t field_keyid;
if (!_mongocrypt_buffer_from_uuid_iter(&field_keyid, &field_iter)) {
CLIENT_ERR("unable to parse uuid key from 'fields.keyId'");
return false;
if (has_keyid) {
BSON_ASSERT(bson_iter_init_find(&field_iter, field, "keyId"));
if (!BSON_ITER_HOLDS_BINARY(&field_iter)) {
CLIENT_ERR("expected 'fields.keyId' to be type binary, got: %d", (int)bson_iter_type(&field_iter));
return false;
}
if (!_mongocrypt_buffer_from_uuid_iter(&field_keyid, &field_iter)) {
CLIENT_ERR("unable to parse uuid key from 'fields.keyId'");
return false;
}
} else if (has_keyaltname) {
BSON_ASSERT(bson_iter_init_find(&field_iter, field, "keyAltName"));
}

const char *keyAltName;
if (has_keyaltname) {
BSON_ASSERT(bson_iter_init_find(&field_iter, field, "keyAltName"));
if (!BSON_ITER_HOLDS_UTF8(&field_iter)) {
CLIENT_ERR("expected 'fields.keyAltName' to be type UTF-8, got: %d", (int)bson_iter_type(&field_iter));
return false;
}
keyAltName = bson_iter_utf8(&field_iter, NULL);
}

const char *field_path;
Expand Down Expand Up @@ -151,7 +179,12 @@ static bool _parse_field(mc_EncryptedFieldConfig_t *efc, bson_t *field, mongocry

/* Prepend a new mc_EncryptedField_t */
mc_EncryptedField_t *ef = bson_malloc0(sizeof(mc_EncryptedField_t));
_mongocrypt_buffer_copy_to(&field_keyid, &ef->keyId);
if (has_keyid) {
_mongocrypt_buffer_copy_to(&field_keyid, &ef->keyId);
}
if (has_keyaltname) {
ef->keyAltName = bson_strdup(keyAltName);
}
ef->path = bson_strdup(field_path);
ef->next = efc->fields;
ef->supported_queries = query_types;
Expand Down
10 changes: 9 additions & 1 deletion src/mc-schema-broker-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include "mc-efc-private.h" // mc_EncryptedFieldConfig_t
#include "mongocrypt-cache-collinfo-private.h"
#include "mongocrypt-key-broker-private.h"
#include "mongocrypt.h"
#include <bson/bson.h>

Expand Down Expand Up @@ -102,6 +103,12 @@ bool mc_schema_broker_need_more_schemas(const mc_schema_broker_t *sb);
const mc_EncryptedFieldConfig_t *
mc_schema_broker_get_encryptedFields(const mc_schema_broker_t *sb, const char *coll, mongocrypt_status_t *status);

// mc_schema_broker_get_encryptedFields returns encryptedFields for a collection if any exists.
//
// Returns NULL if none is found.
const mc_EncryptedFieldConfig_t *
mc_schema_broker_maybe_get_encryptedFields(const mc_schema_broker_t *sb, const char *coll, mongocrypt_status_t *status);

typedef enum {
MC_CMD_SCHEMAS_FOR_CRYPT_SHARED, // target the crypt_shared library.
MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, // target mongocryptd process.
Expand All @@ -118,7 +125,8 @@ typedef enum {
// - encryptionInformation: for QE.
//
// Set cmd_target to the intended command destination. This impacts if/how schema information is added.
bool mc_schema_broker_add_schemas_to_cmd(const mc_schema_broker_t *sb,
bool mc_schema_broker_add_schemas_to_cmd(mc_schema_broker_t *sb,
_mongocrypt_key_broker_t *kb,
bson_t *cmd /* in and out */,
mc_cmd_target_t cmd_target,
mongocrypt_status_t *status);
Expand Down
141 changes: 130 additions & 11 deletions src/mc-schema-broker.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "mc-schema-broker-private.h"

#include "mc-efc-private.h" // mc_EncryptedFieldConfig_t
#include "mongocrypt-buffer-private.h"
#include "mongocrypt-cache-collinfo-private.h"
#include "mongocrypt-key-broker-private.h"
#include "mongocrypt-private.h"
Expand Down Expand Up @@ -607,7 +608,25 @@ mc_schema_broker_get_encryptedFields(const mc_schema_broker_t *sb, const char *c
return NULL;
}

const mc_EncryptedFieldConfig_t *
mc_schema_broker_maybe_get_encryptedFields(const mc_schema_broker_t *sb, const char *coll, mongocrypt_status_t *status) {
BSON_ASSERT_PARAM(sb);
BSON_ASSERT_PARAM(coll);

for (const mc_schema_entry_t *it = sb->ll; it != NULL; it = it->next) {
if (0 != strcmp(it->coll, coll)) {
continue;
}
if (!it->encryptedFields.set) {
return NULL;
}
return &it->encryptedFields.efc;
}
return NULL;
}

static bool append_encryptedFields(const bson_t *encryptedFields,
_mongocrypt_key_broker_t *kb,
const char *coll,
uint8_t default_strEncodeVersion,
bson_t *out,
Expand All @@ -633,7 +652,8 @@ static bool append_encryptedFields(const bson_t *encryptedFields,

// Copy all values. Check if state collections are present.
while (bson_iter_next(&iter)) {
if (strcmp(bson_iter_key(&iter), "escCollection") == 0) {
const char *iter_key = bson_iter_key(&iter);
if (strcmp(iter_key, "escCollection") == 0) {
has_escCollection = true;
}
if (strcmp(bson_iter_key(&iter), "ecocCollection") == 0) {
Expand All @@ -645,8 +665,92 @@ static bool append_encryptedFields(const bson_t *encryptedFields,
if (strcmp(bson_iter_key(&iter), "strEncodeVersion") == 0) {
has_strEncodeVersion = true;
}
TRY_BSON_OR(BSON_APPEND_VALUE(out, bson_iter_key(&iter), bson_iter_value(&iter))) {
goto fail;
/* Special-case the "fields" array: copy each element but omit the
* "keyAltName" key from each subdocument. For other keys, copy as-is. */
if (0 == strcmp(iter_key, "fields") && BSON_ITER_HOLDS_ARRAY(&iter)) {
uint32_t array_len = 0;
const uint8_t *array_data = NULL;
bson_t array_bson;

bson_iter_array(&iter, &array_len, &array_data);
bson_init_static(&array_bson, array_data, array_len);

bson_t new_array;
TRY_BSON_OR(BSON_APPEND_ARRAY_BEGIN(out, "fields", &new_array)) {
goto fail;
}

bson_iter_t arr_it;
if (!bson_iter_init(&arr_it, &array_bson)) {
CLIENT_ERR("failed to iterate 'fields' array");
goto fail;
}

size_t idx = 0;
while (bson_iter_next(&arr_it)) {
char idx_str[32];
const char *idx_str_ptr;
const size_t ret = bson_uint32_to_string((uint32_t)idx, &idx_str_ptr, idx_str, sizeof idx_str);
BSON_ASSERT(ret > 0 && ret <= (int)sizeof idx_str);

if (BSON_ITER_HOLDS_DOCUMENT(&arr_it)) {
uint32_t doc_len = 0;
const uint8_t *doc_data = NULL;
bson_iter_document(&arr_it, &doc_len, &doc_data);
bson_t elem_doc;
bson_init_static(&elem_doc, doc_data, doc_len);

bson_t new_doc;
char *keyAltName_dup = NULL;

/* Extract keyAltName (if present) and strdup it so caller can
* derive a keyId from it. */
bson_iter_t doc_it2;
if (bson_iter_init(&doc_it2, &elem_doc)) {
if (bson_iter_find(&doc_it2, "keyAltName") && BSON_ITER_HOLDS_UTF8(&doc_it2)) {
const char *kan = bson_iter_utf8(&doc_it2, NULL);
if (kan) {
keyAltName_dup = bson_strdup(kan);
}
}
}

bson_init(&new_doc);
/* Copy elem_doc into new_doc excluding "keyAltName". */
bson_copy_to_excluding_noinit(&elem_doc, &new_doc, "keyAltName", NULL);

if (keyAltName_dup) {
_mongocrypt_buffer_t unused, key_id_out;
bson_value_t key_alt_name_v;
_bson_value_from_string(keyAltName_dup, &key_alt_name_v);
BSON_ASSERT(_mongocrypt_key_broker_decrypted_key_by_name(kb, &key_alt_name_v, &unused, &key_id_out));
bson_append_binary(&new_doc, "keyId", -1, key_id_out.subtype, key_id_out.data, key_id_out.len);
}

TRY_BSON_OR(bson_append_document(&new_array, idx_str_ptr, -1, &new_doc)) {
bson_destroy(&new_doc);
bson_free(keyAltName_dup);
goto fail;
}
bson_destroy(&new_doc);
bson_free(keyAltName_dup);
} else {
/* Non-document elements: copy as-is. */
TRY_BSON_OR(BSON_APPEND_VALUE(&new_array, idx_str, bson_iter_value(&arr_it))) {
goto fail;
}
}

idx++;
}

TRY_BSON_OR(bson_append_array_end(out, &new_array)) {
goto fail;
}
} else {
TRY_BSON_OR(BSON_APPEND_VALUE(out, iter_key, bson_iter_value(&iter))) {
goto fail;
}
}
}

Expand Down Expand Up @@ -687,6 +791,7 @@ static bool append_encryptedFields(const bson_t *encryptedFields,
}

static bool append_encryptionInformation(const mc_schema_broker_t *sb,
_mongocrypt_key_broker_t *kb,
const char *cmd_name,
bson_t *out,
mongocrypt_status_t *status) {
Expand Down Expand Up @@ -728,7 +833,7 @@ static bool append_encryptionInformation(const mc_schema_broker_t *sb,
encryptedFields = &se->encryptedFields.bson;
default_strEncodeVersion = se->encryptedFields.efc.str_encode_version;
}
if (!append_encryptedFields(encryptedFields, se->coll, default_strEncodeVersion, &ns_to_schema_bson, status)) {
if (!append_encryptedFields(encryptedFields, kb, se->coll, default_strEncodeVersion, &ns_to_schema_bson, status)) {
goto loop_fail;
}

Expand Down Expand Up @@ -778,6 +883,7 @@ static const char *get_cmd_name(const bson_t *cmd, mongocrypt_status_t *status)
}

static bool insert_encryptionInformation(const mc_schema_broker_t *sb,
_mongocrypt_key_broker_t *kb,
const char *cmd_name,
bson_t *cmd /* in and out */,
mc_cmd_target_t cmd_target,
Expand Down Expand Up @@ -841,7 +947,7 @@ static bool insert_encryptionInformation(const mc_schema_broker_t *sb,
goto fail;
}
// And append `encryptionInformation`.
if (!append_encryptionInformation(sb, cmd_name, &nsInfo_array_0, status)) {
if (!append_encryptionInformation(sb, kb, cmd_name, &nsInfo_array_0, status)) {
goto fail;
}
if (!bson_append_document_end(&nsInfo_array, &nsInfo_array_0)) {
Expand Down Expand Up @@ -891,7 +997,7 @@ static bool insert_encryptionInformation(const mc_schema_broker_t *sb,
bson_copy_to(&tmp, &explain);
}

if (!append_encryptionInformation(sb, cmd_name, &explain, status)) {
if (!append_encryptionInformation(sb, kb, cmd_name, &explain, status)) {
goto fail;
}

Expand All @@ -914,7 +1020,7 @@ static bool insert_encryptionInformation(const mc_schema_broker_t *sb,
// "<command name>": { ... }
// "encryptionInformation": {}
// }
if (!append_encryptionInformation(sb, cmd_name, cmd, status)) {
if (!append_encryptionInformation(sb, kb, cmd_name, cmd, status)) {
goto fail;
}

Expand Down Expand Up @@ -1032,7 +1138,8 @@ static bool insert_csfleEncryptionSchemas(const mc_schema_broker_t *sb,
return true;
}

bool mc_schema_broker_add_schemas_to_cmd(const mc_schema_broker_t *sb,
bool mc_schema_broker_add_schemas_to_cmd(mc_schema_broker_t *sb,
_mongocrypt_key_broker_t *kb,
bson_t *cmd /* in and out */,
mc_cmd_target_t cmd_target,
mongocrypt_status_t *status) {
Expand All @@ -1053,6 +1160,18 @@ bool mc_schema_broker_add_schemas_to_cmd(const mc_schema_broker_t *sb,
if (it->encryptedFields.set) {
has_encryptedFields = true;
coll_with_encryptedFields = it->coll;
for (mc_EncryptedField_t *f = it->encryptedFields.efc.fields; f != NULL; f = f->next) {
if (f->keyAltName) {
bson_value_t key_alt_name;
_mongocrypt_buffer_t unused;
_bson_value_from_string(f->keyAltName, &key_alt_name);
const bool r = _mongocrypt_key_broker_decrypted_key_by_name(kb, &key_alt_name, &unused, &f->keyId);
if (!r) {
CLIENT_ERR("Could not find key by keyAltName: %s", f->keyAltName);
return false;
}
}
}
} else if (it->jsonSchema.set) {
has_jsonSchema = true;
coll_with_jsonSchema = it->coll;
Expand All @@ -1061,7 +1180,7 @@ bool mc_schema_broker_add_schemas_to_cmd(const mc_schema_broker_t *sb,

if (has_encryptedFields && has_jsonSchema) {
if (sb->schema_mixing_is_supported) {
return insert_encryptionInformation(sb, cmd_name, cmd, cmd_target, status)
return insert_encryptionInformation(sb, kb, cmd_name, cmd, cmd_target, status)
&& insert_csfleEncryptionSchemas(sb, cmd, cmd_target, status);
}

Expand All @@ -1078,7 +1197,7 @@ bool mc_schema_broker_add_schemas_to_cmd(const mc_schema_broker_t *sb,

if (has_encryptedFields) {
// Use encryptionInformation.
return insert_encryptionInformation(sb, cmd_name, cmd, cmd_target, status);
return insert_encryptionInformation(sb, kb, cmd_name, cmd, cmd_target, status);
}

if (has_jsonSchema) {
Expand All @@ -1089,7 +1208,7 @@ bool mc_schema_broker_add_schemas_to_cmd(const mc_schema_broker_t *sb,
// Collections have no QE or CSFLE schemas.
if (0 == strcmp(cmd_name, "bulkWrite")) {
// "bulkWrite" does not support the jsonSchema field. Use encryptionInformation with empty schemas.
return insert_encryptionInformation(sb, cmd_name, cmd, cmd_target, status);
return insert_encryptionInformation(sb, kb, cmd_name, cmd, cmd_target, status);
}

// Use csfleEncryptionSchemas / jsonSchema with empty schemas.
Expand Down
Loading