summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorsergey.galtsev <sergey.galtsev@mongodb.com>2022-04-01 22:07:58 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-04-01 23:07:50 +0000
commiteea86b56a99c0bde0c387ab45d42a4ac156e9750 (patch)
tree4494de09163b7fe7dd8675c5a6ddffe6d5c95d50 /src
parent9eaa9b280cf9c35d83507e3ca3a364721195f5d5 (diff)
downloadmongo-eea86b56a99c0bde0c387ab45d42a4ac156e9750.tar.gz
SERVER-63736 validate only fields listed in config are indexed
Diffstat (limited to 'src')
-rw-r--r--src/mongo/crypto/fle_crypto.cpp23
-rw-r--r--src/mongo/crypto/fle_crypto.h5
-rw-r--r--src/mongo/crypto/fle_crypto_test.cpp97
-rw-r--r--src/mongo/db/fle_crud.cpp16
4 files changed, 95 insertions, 46 deletions
diff --git a/src/mongo/crypto/fle_crypto.cpp b/src/mongo/crypto/fle_crypto.cpp
index 0dacb9c0751..9303bcbb61f 100644
--- a/src/mongo/crypto/fle_crypto.cpp
+++ b/src/mongo/crypto/fle_crypto.cpp
@@ -1990,10 +1990,29 @@ ESCDerivedFromDataTokenAndContentionFactorToken EDCServerPayloadInfo::getESCToke
payload.getEscDerivedToken());
}
+void EDCServerCollection::validateEncryptedFieldInfo(BSONObj& obj,
+ const EncryptedFieldConfig& efc) {
+ stdx::unordered_set<std::string> indexedFields;
+ for (auto f : efc.getFields()) {
+ if (f.getQueries().has_value()) {
+ indexedFields.insert(f.getPath().toString());
+ }
+ }
+
+ visitEncryptedBSON(obj, [&indexedFields](ConstDataRange cdr, StringData fieldPath) {
+ auto [encryptedTypeBinding, subCdr] = fromEncryptedConstDataRange(cdr);
+ if (encryptedTypeBinding == EncryptedBinDataType::kFLE2InsertUpdatePayload) {
+ uassert(6373601,
+ str::stream() << "Field '" << fieldPath
+ << "' is encrypted, but absent from schema",
+ indexedFields.contains(fieldPath.toString()));
+ }
+ });
+}
+
+
std::vector<EDCServerPayloadInfo> EDCServerCollection::getEncryptedFieldInfo(BSONObj& obj) {
std::vector<EDCServerPayloadInfo> fields;
- // TODO (SERVER-63736) - Validate only fields listed in EncryptedFieldConfig are indexed
-
visitEncryptedBSON(obj, [&fields](ConstDataRange cdr, StringData fieldPath) {
collectEDCServerInfo(&fields, cdr, fieldPath);
});
diff --git a/src/mongo/crypto/fle_crypto.h b/src/mongo/crypto/fle_crypto.h
index ab812b169ac..1092a817d82 100644
--- a/src/mongo/crypto/fle_crypto.h
+++ b/src/mongo/crypto/fle_crypto.h
@@ -994,6 +994,11 @@ struct FLEDeleteToken {
class EDCServerCollection {
public:
/**
+ * Validate that payload is compatible with schema
+ */
+ static void validateEncryptedFieldInfo(BSONObj& obj, const EncryptedFieldConfig& efc);
+
+ /**
* Get information about all FLE2InsertUpdatePayload payloads
*/
static std::vector<EDCServerPayloadInfo> getEncryptedFieldInfo(BSONObj& obj);
diff --git a/src/mongo/crypto/fle_crypto_test.cpp b/src/mongo/crypto/fle_crypto_test.cpp
index 8997f88e58c..01d5e1c13a7 100644
--- a/src/mongo/crypto/fle_crypto_test.cpp
+++ b/src/mongo/crypto/fle_crypto_test.cpp
@@ -598,10 +598,15 @@ std::vector<char> generatePlaceholder(
return v;
}
-
-BSONObj encryptDocument(BSONObj obj, FLEKeyVault* keyVault) {
+BSONObj encryptDocument(BSONObj obj,
+ FLEKeyVault* keyVault,
+ const EncryptedFieldConfig* efc = nullptr) {
auto result = FLEClientCrypto::transformPlaceholders(obj, keyVault);
+ if (nullptr != efc) {
+ EDCServerCollection::validateEncryptedFieldInfo(result, *efc);
+ }
+
// Start Server Side
auto serverPayload = EDCServerCollection::getEncryptedFieldInfo(result);
@@ -1080,43 +1085,39 @@ bool vectorContains(const std::vector<T>& vec, Func func) {
EncryptedFieldConfig getTestEncryptedFieldConfig() {
constexpr auto schema = R"({
- "escCollection": "esc",
- "eccCollection": "ecc",
- "ecocCollection": "ecoc",
- "fields": [
- {
- "keyId":
- {
- "$uuid": "12345678-1234-9876-1234-123456789012"
- }
- ,
- "path": "encrypted",
- "bsonType": "string",
- "queries": {"queryType": "equality"}
-
- },
- {
- "keyId":
- {
- "$uuid": "12345678-1234-9876-1234-123456789013"
- }
- ,
- "path": "nested.encrypted",
- "bsonType": "string",
- "queries": {"queryType": "equality"}
-
- },
- {
- "keyId":
- {
- "$uuid": "12345678-1234-9876-1234-123456789014"
- }
- ,
- "path": "nested.notindexed",
- "bsonType": "string"
- }
- ]
-})";
+ "escCollection": "esc",
+ "eccCollection": "ecc",
+ "ecocCollection": "ecoc",
+ "fields": [
+ {
+ "keyId": {
+ "$uuid": "12345678-1234-9876-1234-123456789012"
+ },
+ "path": "encrypted",
+ "bsonType": "string",
+ "queries": {
+ "queryType": "equality"
+ }
+ },
+ {
+ "keyId": {
+ "$uuid": "12345678-1234-9876-1234-123456789013"
+ },
+ "path": "nested.encrypted",
+ "bsonType": "string",
+ "queries": {
+ "queryType": "equality"
+ }
+ },
+ {
+ "keyId": {
+ "$uuid": "12345678-1234-9876-1234-123456789014"
+ },
+ "path": "nested.notindexed",
+ "bsonType": "string"
+ }
+ ]
+ })";
return EncryptedFieldConfig::parse(IDLParserErrorContext("root"), fromjson(schema));
}
@@ -1433,7 +1434,7 @@ TEST(EDC, ValidateDocument) {
builder.appendBinData("notindexed", buf.size(), BinDataType::Encrypt, buf.data());
}
- auto finalDoc = encryptDocument(builder.obj(), &keyVault);
+ auto finalDoc = encryptDocument(builder.obj(), &keyVault, &efc);
// Positive - Encrypted Doc
FLEClientCrypto::validateDocument(finalDoc, efc, &keyVault);
@@ -1495,6 +1496,22 @@ TEST(EDC, ValidateDocument) {
}
}
+TEST(EDC, NonMatchingSchema) {
+ EncryptedFieldConfig efc = getTestEncryptedFieldConfig();
+
+ TestKeyVault keyVault;
+
+ BSONObjBuilder builder;
+ builder.append("plainText", "sample");
+ auto doc = BSON("a"
+ << "not really a secret");
+ auto element = doc.firstElement();
+ auto buf = generatePlaceholder(element, Operation::kInsert);
+ builder.appendBinData("not-encrypted", buf.size(), BinDataType::Encrypt, buf.data());
+
+ ASSERT_THROWS_CODE(encryptDocument(builder.obj(), &keyVault, &efc), DBException, 6373601);
+}
+
BSONObj encryptUpdateDocument(BSONObj obj, FLEKeyVault* keyVault) {
auto result = FLEClientCrypto::transformPlaceholders(obj, keyVault);
diff --git a/src/mongo/db/fle_crud.cpp b/src/mongo/db/fle_crud.cpp
index 2451133e2f4..2a6e2243bb9 100644
--- a/src/mongo/db/fle_crud.cpp
+++ b/src/mongo/db/fle_crud.cpp
@@ -195,11 +195,17 @@ std::pair<FLEBatchResult, write_ops::InsertCommandReply> processInsert(
const write_ops::InsertCommandRequest& insertRequest,
GetTxnCallback getTxns) {
+ auto edcNss = insertRequest.getNamespace();
+ auto ei = insertRequest.getEncryptionInformation().get();
+
+ auto efc = EncryptionInformationHelpers::getAndValidateSchema(edcNss, ei);
+
auto documents = insertRequest.getDocuments();
// TODO - how to check if a document will be too large???
uassert(6371202, "Only single insert batches are supported in FLE2", documents.size() == 1);
auto document = documents[0];
+ EDCServerCollection::validateEncryptedFieldInfo(document, efc);
auto serverPayload = std::make_shared<std::vector<EDCServerPayloadInfo>>(
EDCServerCollection::getEncryptedFieldInfo(document));
@@ -209,10 +215,6 @@ std::pair<FLEBatchResult, write_ops::InsertCommandReply> processInsert(
FLEBatchResult::kNotProcessed, write_ops::InsertCommandReply()};
}
- auto ei = insertRequest.getEncryptionInformation().get();
-
- auto edcNss = insertRequest.getNamespace();
- auto efc = EncryptionInformationHelpers::getAndValidateSchema(insertRequest.getNamespace(), ei);
write_ops::InsertCommandReply reply;
std::shared_ptr<txn_api::TransactionWithRetries> trun = getTxns(opCtx);
@@ -712,6 +714,8 @@ write_ops::UpdateCommandReply processUpdate(FLEQueryInterface* queryImpl,
if (updateModification.type() == write_ops::UpdateModification::Type::kModifier) {
auto updateModifier = updateModification.getUpdateModifier();
+ auto setObject = updateModifier.getObjectField("$set");
+ EDCServerCollection::validateEncryptedFieldInfo(setObject, efc);
serverPayload = EDCServerCollection::getEncryptedFieldInfo(updateModifier);
processFieldsForInsert(queryImpl, edcNss, serverPayload, efc);
@@ -723,6 +727,7 @@ write_ops::UpdateCommandReply processUpdate(FLEQueryInterface* queryImpl,
pushUpdate, write_ops::UpdateModification::ClassicTag(), false));
} else {
auto replacementDocument = updateModification.getUpdateReplacement();
+ EDCServerCollection::validateEncryptedFieldInfo(replacementDocument, efc);
serverPayload = EDCServerCollection::getEncryptedFieldInfo(replacementDocument);
processFieldsForInsert(queryImpl, edcNss, serverPayload, efc);
@@ -869,6 +874,8 @@ write_ops::FindAndModifyCommandReply processFindAndModify(
if (updateModification.type() == write_ops::UpdateModification::Type::kModifier) {
auto updateModifier = updateModification.getUpdateModifier();
+ auto setObject = updateModifier.getObjectField("$set");
+ EDCServerCollection::validateEncryptedFieldInfo(setObject, efc);
serverPayload = EDCServerCollection::getEncryptedFieldInfo(updateModifier);
processFieldsForInsert(queryImpl, edcNss, serverPayload, efc);
@@ -879,6 +886,7 @@ write_ops::FindAndModifyCommandReply processFindAndModify(
pushUpdate, write_ops::UpdateModification::ClassicTag(), false);
} else {
auto replacementDocument = updateModification.getUpdateReplacement();
+ EDCServerCollection::validateEncryptedFieldInfo(replacementDocument, efc);
serverPayload = EDCServerCollection::getEncryptedFieldInfo(replacementDocument);
processFieldsForInsert(queryImpl, edcNss, serverPayload, efc);