diff --git a/fdbserver/CommitProxyServer.actor.cpp b/fdbserver/CommitProxyServer.actor.cpp index 8272cb3c07..5c8449a332 100644 --- a/fdbserver/CommitProxyServer.actor.cpp +++ b/fdbserver/CommitProxyServer.actor.cpp @@ -236,6 +236,105 @@ struct ResolutionRequestBuilder { } }; +ErrorOr> getTenantEntry(ProxyCommitData* commitData, + Optional tenant, + Optional tenantId, + bool logOnFailure) { + if (tenant.present()) { + auto itr = commitData->tenantMap.find(tenant.get()); + if (itr == commitData->tenantMap.end()) { + if (logOnFailure) { + TraceEvent(SevWarn, "CommitProxyUnknownTenant", commitData->dbgid).detail("Tenant", tenant.get()); + } + + return unknown_tenant(); + } else if (tenantId.present() && tenantId.get() != itr->second.id) { + if (logOnFailure) { + TraceEvent(SevWarn, "CommitProxyTenantIdMismatch", commitData->dbgid) + .detail("Tenant", tenant.get()) + .detail("TenantId", tenantId) + .detail("ExistingId", itr->second.id); + } + + return unknown_tenant(); + } + + return ErrorOr>(Optional(itr->second)); + } + + return Optional(); +} + +bool verifyTenantPrefix(ProxyCommitData* const commitData, const CommitTransactionRequest& req) { + ErrorOr> tenantEntry = + getTenantEntry(commitData, req.tenantInfo.name.castTo(), req.tenantInfo.tenantId, true); + + if (tenantEntry.isError()) { + return true; + } + + if (tenantEntry.get().present()) { + Key tenantPrefix = tenantEntry.get().get().prefix; + for (auto& m : req.transaction.mutations) { + if (m.param1 != metadataVersionKey) { + if (!m.param1.startsWith(tenantPrefix)) { + TraceEvent(SevWarnAlways, "TenantPrefixMismatch") + .suppressFor(60) + .detail("Prefix", tenantPrefix.toHexString()) + .detail("Key", m.param1.toHexString()); + return false; + } + + if (m.type == MutationRef::ClearRange && !m.param2.startsWith(tenantPrefix)) { + TraceEvent(SevWarnAlways, "TenantClearRangePrefixMismatch") + .suppressFor(60) + .detail("Prefix", tenantPrefix.toHexString()) + .detail("Key", m.param2.toHexString()); + return false; + } else if (m.type == MutationRef::SetVersionstampedKey) { + ASSERT(m.param1.size() >= 4); + uint8_t* key = const_cast(m.param1.begin()); + int* offset = reinterpret_cast(&key[m.param1.size() - 4]); + if (*offset < tenantPrefix.size()) { + TraceEvent(SevWarnAlways, "TenantVersionstampInvalidOffset") + .suppressFor(60) + .detail("Prefix", tenantPrefix.toHexString()) + .detail("Key", m.param1.toHexString()) + .detail("Offset", *offset); + return false; + } + } + } + } + + for (auto& rc : req.transaction.read_conflict_ranges) { + if (rc.begin != metadataVersionKey && + (!rc.begin.startsWith(tenantPrefix) || !rc.end.startsWith(tenantPrefix))) { + TraceEvent(SevWarnAlways, "TenantReadConflictPrefixMismatch") + .suppressFor(60) + .detail("Prefix", tenantPrefix.toHexString()) + .detail("BeginKey", rc.begin.toHexString()) + .detail("EndKey", rc.end.toHexString()); + return false; + } + } + + for (auto& wc : req.transaction.write_conflict_ranges) { + if (wc.begin != metadataVersionKey && + (!wc.begin.startsWith(tenantPrefix) || !wc.end.startsWith(tenantPrefix))) { + TraceEvent(SevWarnAlways, "TenantWriteConflictPrefixMismatch") + .suppressFor(60) + .detail("Prefix", tenantPrefix.toHexString()) + .detail("BeginKey", wc.begin.toHexString()) + .detail("EndKey", wc.end.toHexString()); + return false; + } + } + } + + return true; +} + ACTOR Future commitBatcher(ProxyCommitData* commitData, PromiseStream, int>> out, FutureStream in, @@ -282,6 +381,13 @@ ACTOR Future commitBatcher(ProxyCommitData* commitData, .detail("Size", bytes) .detail("Client", req.reply.getEndpoint().getPrimaryAddress()); } + + if (!verifyTenantPrefix(commitData, req)) { + ++commitData->stats.txnCommitErrors; + req.reply.sendError(illegal_tenant_access()); + continue; + } + ++commitData->stats.txnCommitIn; if (req.debugID.present()) { @@ -450,35 +556,6 @@ ACTOR static Future trackResolutionMetrics(Referen return reply; } -ErrorOr> getTenantEntry(ProxyCommitData* commitData, - Optional tenant, - Optional tenantId, - bool logOnFailure) { - if (tenant.present()) { - auto itr = commitData->tenantMap.find(tenant.get()); - if (itr == commitData->tenantMap.end()) { - if (logOnFailure) { - TraceEvent(SevWarn, "CommitProxyUnknownTenant", commitData->dbgid).detail("Tenant", tenant.get()); - } - - return unknown_tenant(); - } else if (tenantId.present() && tenantId.get() != itr->second.id) { - if (logOnFailure) { - TraceEvent(SevWarn, "CommitProxyTenantIdMismatch", commitData->dbgid) - .detail("Tenant", tenant.get()) - .detail("TenantId", tenantId) - .detail("ExistingId", itr->second.id); - } - - return unknown_tenant(); - } - - return ErrorOr>(Optional(itr->second)); - } - - return Optional(); -} - namespace CommitBatch { struct CommitBatchContext { diff --git a/flow/error_definitions.h b/flow/error_definitions.h index a26d288598..0fee428000 100755 --- a/flow/error_definitions.h +++ b/flow/error_definitions.h @@ -230,6 +230,7 @@ ERROR( invalid_tenant_name, 2134, "Tenant name cannot begin with \\xff"); ERROR( tenant_prefix_allocator_conflict, 2135, "The database already has keys stored at the prefix allocated for the tenant"); ERROR( tenants_disabled, 2136, "Tenants have been disabled in the cluster"); ERROR( unknown_tenant, 2137, "Tenant is not available from this server") +ERROR( illegal_tenant_access, 2138, "Illegal tenant access") // 2200 - errors from bindings and official APIs ERROR( api_version_unset, 2200, "API version is not set" )