0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-11-26 00:32:35 +01:00

ircd:Ⓜ️:vm: Add redacted canonization cases prior to eval.

This commit is contained in:
Jason Volk 2020-11-29 19:46:20 -08:00
parent be7bab0c16
commit d49227c848
5 changed files with 102 additions and 53 deletions

View file

@ -95,6 +95,7 @@ struct ircd::m::vm::eval
hook::base *hook {nullptr}; hook::base *hook {nullptr};
vm::phase phase {vm::phase(0)}; vm::phase phase {vm::phase(0)};
bool room_internal {false}; bool room_internal {false};
bool redacted {false};
void mfetch_keys() const; void mfetch_keys() const;

View file

@ -115,7 +115,7 @@ ircd::m::vm::conform_report
[](const m::event &event, eval &eval) [](const m::event &event, eval &eval)
{ {
assert(eval.opts); assert(eval.opts);
auto &opts(*eval.opts); const auto &opts(*eval.opts);
// When opts.conformed is set the report is already generated // When opts.conformed is set the report is already generated
if(opts.conformed) if(opts.conformed)
@ -124,17 +124,30 @@ ircd::m::vm::conform_report
return; return;
} }
// Mask of checks to be bypassed
auto non_conform
{
opts.non_conform
};
// This hook is called prior to event_id determination; must be skipped
non_conform.set(event::conforms::INVALID_OR_MISSING_EVENT_ID);
// For internal rooms for now.
if(eval.room_internal)
non_conform.set(event::conforms::MISMATCH_ORIGIN_SENDER);
// Generate the report here. // Generate the report here.
eval.report = event::conforms eval.report = event::conforms
{ {
event, opts.non_conform.report event, non_conform.report
}; };
// When opts.conforming is false a bad report is not an error. // When opts.conforming is false a bad report is not an error.
if(!opts.conforming) if(!opts.conforming)
return; return;
const bool redacted eval.redacted =
{ {
// redacted hint given in options // redacted hint given in options
opts.redacted != -1? opts.redacted != -1?
@ -149,24 +162,33 @@ ircd::m::vm::conform_report
false: false:
// assume redacted when hash mismatch already allowed // assume redacted when hash mismatch already allowed
(opts.non_conform.has(event::conforms::MISMATCH_HASHES))? non_conform.has(event::conforms::MISMATCH_HASHES)?
true: true:
// assume no redaction for hash match // assume no redaction for hash match
(!eval.report.has(event::conforms::MISMATCH_HASHES))? !eval.report.has(event::conforms::MISMATCH_HASHES)?
false: false:
// case for authoritative redaction
eval.report.has(event::conforms::MISMATCH_HASHES)
&& opts.node_id == json::get<"origin"_>(event)?
true:
// make query // make query
bool(m::redacted(event.event_id)) event.event_id?
bool(m::redacted(event.event_id)):
// otherwise deny redacted
false
}; };
auto report auto report
{ {
eval.report eval.report
};; };
// Allow content hash to fail on redacted events. // Allow content hash to fail on redacted events.
if(redacted) if(eval.redacted)
report.del(event::conforms::MISMATCH_HASHES); report.del(event::conforms::MISMATCH_HASHES);
// Otherwise this will kill the eval // Otherwise this will kill the eval

View file

@ -328,7 +328,7 @@ ircd::m::commit(const room &room,
// Some functionality on this server may create an event on behalf // Some functionality on this server may create an event on behalf
// of remote users. It's safe for us to mask this here, but eval'ing // of remote users. It's safe for us to mask this here, but eval'ing
// this event in any replay later will require special casing. // this event in any replay later will require special casing.
opts.non_conform |= event::conforms::MISMATCH_ORIGIN_SENDER; opts.non_conform.set(event::conforms::MISMATCH_ORIGIN_SENDER);
// Don't need this here // Don't need this here
opts.phase.reset(vm::phase::VERIFY); opts.phase.reset(vm::phase::VERIFY);

View file

@ -218,32 +218,16 @@ try
*eval.opts *eval.opts
}; };
assert(!eval.buf || size(eval.buf) >= event::MAX_SIZE); const scope_restore eval_report
if(!opts.json_source && !eval.buf)
eval.buf = unique_mutable_buffer
{ {
event::MAX_SIZE, 64 eval.report, event::conforms{}
}; };
const scope_restore event_source const scope_restore eval_redacted
{ {
mutable_cast(event).source, eval.redacted, false
// Canonize from some other serialized source.
!opts.json_source && event.source?
json::object(json::stringify(mutable_buffer{eval.buf}, event.source)):
// Canonize from no source; usually taken when my(event).
// XXX elision conditions go here
!opts.json_source?
json::object(json::stringify(mutable_buffer{eval.buf}, event)):
// Use the input directly.
event.source
}; };
// Set a member to the room_id for convenient access, without stepping on
// any room_id reference that exists there for whatever reason.
const scope_restore eval_room_id const scope_restore eval_room_id
{ {
eval.room_id, eval.room_id,
@ -253,7 +237,7 @@ try
eval.room_id: eval.room_id:
// Use the room_id in the event // Use the room_id in the event
likely(valid(id::ROOM, json::get<"room_id"_>(event)))? likely(json::get<"room_id"_>(event))?
string_view(json::get<"room_id"_>(event)): string_view(json::get<"room_id"_>(event)):
eval.room_id, eval.room_id,
@ -288,6 +272,68 @@ try
false false
}; };
// These checks only require the event data itself.
if(likely(opts.phase[phase::CONFORM]) && !opts.edu)
{
const scope_restore eval_phase
{
eval.phase, phase::CONFORM
};
const ctx::critical_assertion ca;
call_hook(conform_hook, eval, event, eval);
}
assert(!eval.buf || size(eval.buf) >= event::MAX_SIZE);
const bool redacted
{
eval.redacted
|| eval.report.has(event::conforms::MISMATCH_HASHES)
};
if(!opts.edu && !eval.buf && (!opts.json_source || redacted))
eval.buf = unique_mutable_buffer
{
event::MAX_SIZE, 64
};
const scope_restore event_source
{
mutable_cast(event).source,
// Canonize and redact from some other serialized source.
!opts.edu && !opts.json_source && event.source && redacted?
json::stringify(mutable_buffer{eval.buf}, m::essential(event.source, event::buf[0])):
// Canonize and redact from no source.
!opts.edu && !opts.json_source && redacted?
json::stringify(mutable_buffer{eval.buf}, m::essential(event, event::buf[0])):
// Canonize from some other serialized source.
likely(!opts.edu && !opts.json_source && event.source)?
json::stringify(mutable_buffer{eval.buf}, event.source):
// Canonize from no source; usually taken when my(event).
// XXX elision conditions go here
likely(!opts.edu && !opts.json_source)?
json::stringify(mutable_buffer{eval.buf}, event):
// Use the input directly.
string_view{event.source}
};
const scope_restore eval_room_id_reref
{
eval.room_id,
// Re-assigns reference after any prior rewrites
likely(json::get<"room_id"_>(event))?
string_view(json::get<"room_id"_>(event)):
// No action
eval.room_id,
};
// Procure the room version. // Procure the room version.
char room_version_buf[room::VERSION_MAX_SIZE]; char room_version_buf[room::VERSION_MAX_SIZE];
const scope_restore eval_room_version const scope_restore eval_room_version
@ -648,19 +694,6 @@ ircd::m::vm::execute_pdu(eval &eval,
fault::GENERAL, "Internal room event denied from external source." fault::GENERAL, "Internal room event denied from external source."
}; };
// The conform hook runs static checks on an event's formatting and
// composure; these checks only require the event data itself.
if(likely(opts.phase[phase::CONFORM]))
{
const scope_restore eval_phase
{
eval.phase, phase::CONFORM
};
const ctx::critical_assertion ca;
call_hook(conform_hook, eval, event, eval);
}
// Wait for any pending duplicate evals before proceeding. // Wait for any pending duplicate evals before proceeding.
assert(eval::count(event_id)); assert(eval::count(event_id));
if(likely(opts.phase[phase::DUPCHK] && opts.unique)) if(likely(opts.phase[phase::DUPCHK] && opts.unique))

View file

@ -349,7 +349,6 @@ try
opts.warnlog &= ~vm::fault::EXISTS; opts.warnlog &= ~vm::fault::EXISTS;
opts.notify_servers = false; opts.notify_servers = false;
opts.node_id = origin; opts.node_id = origin;
log::debug log::debug
{ {
log, "Evaluating auth chain for %s in %s events:%zu", log, "Evaluating auth chain for %s in %s events:%zu",
@ -475,12 +474,6 @@ try
opts.phase.set(m::vm::phase::FETCH_STATE, false); opts.phase.set(m::vm::phase::FETCH_STATE, false);
opts.node_id = result.origin; opts.node_id = result.origin;
opts.notify_servers = false; opts.notify_servers = false;
// The result won't give us events with a content hash mismatch unless
// they were obtained from an authoritative source. For this we can
// unconditionally allow hash mismatch from here.
opts.redacted = 1;
vm::eval vm::eval
{ {
pdus, opts pdus, opts