diff --git a/include/ircd/m/vm.h b/include/ircd/m/vm.h index 911882085..a269f2977 100644 --- a/include/ircd/m/vm.h +++ b/include/ircd/m/vm.h @@ -95,6 +95,7 @@ struct ircd::m::vm::eval hook::base *hook {nullptr}; vm::phase phase {vm::phase(0)}; bool room_internal {false}; + bool redacted {false}; void mfetch_keys() const; diff --git a/matrix/event_conforms.cc b/matrix/event_conforms.cc index f59c38b28..f2160f0bc 100644 --- a/matrix/event_conforms.cc +++ b/matrix/event_conforms.cc @@ -115,7 +115,7 @@ ircd::m::vm::conform_report [](const m::event &event, eval &eval) { assert(eval.opts); - auto &opts(*eval.opts); + const auto &opts(*eval.opts); // When opts.conformed is set the report is already generated if(opts.conformed) @@ -124,17 +124,30 @@ ircd::m::vm::conform_report 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. 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. if(!opts.conforming) return; - const bool redacted + eval.redacted = { // redacted hint given in options opts.redacted != -1? @@ -149,24 +162,33 @@ ircd::m::vm::conform_report false: // assume redacted when hash mismatch already allowed - (opts.non_conform.has(event::conforms::MISMATCH_HASHES))? + non_conform.has(event::conforms::MISMATCH_HASHES)? true: // assume no redaction for hash match - (!eval.report.has(event::conforms::MISMATCH_HASHES))? + !eval.report.has(event::conforms::MISMATCH_HASHES)? false: + // case for authoritative redaction + eval.report.has(event::conforms::MISMATCH_HASHES) + && opts.node_id == json::get<"origin"_>(event)? + true: + // make query - bool(m::redacted(event.event_id)) + event.event_id? + bool(m::redacted(event.event_id)): + + // otherwise deny redacted + false }; auto report { eval.report - };; + }; // Allow content hash to fail on redacted events. - if(redacted) + if(eval.redacted) report.del(event::conforms::MISMATCH_HASHES); // Otherwise this will kill the eval diff --git a/matrix/room.cc b/matrix/room.cc index 8fb7e0c9a..3ec37c627 100644 --- a/matrix/room.cc +++ b/matrix/room.cc @@ -328,7 +328,7 @@ ircd::m::commit(const room &room, // 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 // 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 opts.phase.reset(vm::phase::VERIFY); diff --git a/matrix/vm_execute.cc b/matrix/vm_execute.cc index c5bc6958c..3a3bba660 100644 --- a/matrix/vm_execute.cc +++ b/matrix/vm_execute.cc @@ -218,32 +218,16 @@ try *eval.opts }; - assert(!eval.buf || size(eval.buf) >= event::MAX_SIZE); - if(!opts.json_source && !eval.buf) - eval.buf = unique_mutable_buffer - { - event::MAX_SIZE, 64 - }; - - const scope_restore event_source + const scope_restore eval_report { - mutable_cast(event).source, - - // 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 + eval.report, event::conforms{} + }; + + const scope_restore eval_redacted + { + eval.redacted, false }; - // 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 { eval.room_id, @@ -253,7 +237,7 @@ try eval.room_id: // 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)): eval.room_id, @@ -288,6 +272,68 @@ try 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. char room_version_buf[room::VERSION_MAX_SIZE]; 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." }; - // 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. assert(eval::count(event_id)); if(likely(opts.phase[phase::DUPCHK] && opts.unique)) diff --git a/matrix/vm_fetch.cc b/matrix/vm_fetch.cc index f4e0df3c9..714b1575d 100644 --- a/matrix/vm_fetch.cc +++ b/matrix/vm_fetch.cc @@ -349,7 +349,6 @@ try opts.warnlog &= ~vm::fault::EXISTS; opts.notify_servers = false; opts.node_id = origin; - log::debug { 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.node_id = result.origin; 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 { pdus, opts