diff --git a/matrix/Makefile.am b/matrix/Makefile.am index 96c5bed0a..0a4f915be 100644 --- a/matrix/Makefile.am +++ b/matrix/Makefile.am @@ -189,6 +189,7 @@ libircd_matrix_la_SOURCES += vm_eval.cc libircd_matrix_la_SOURCES += vm_inject.cc libircd_matrix_la_SOURCES += vm_execute.cc libircd_matrix_la_SOURCES += vm_fetch.cc +libircd_matrix_la_SOURCES += vm_conforms.cc libircd_matrix_la_SOURCES += init_backfill.cc libircd_matrix_la_SOURCES += homeserver.cc libircd_matrix_la_SOURCES += homeserver_bootstrap.cc diff --git a/matrix/event_conforms.cc b/matrix/event_conforms.cc index 56b48df4b..513eafbae 100644 --- a/matrix/event_conforms.cc +++ b/matrix/event_conforms.cc @@ -8,189 +8,6 @@ // copyright notice and this permission notice is present in all copies. The // full license for this software is available in the LICENSE file. -namespace ircd::m::vm -{ - extern m::hookfn conform_check_event_id; - extern m::hookfn conform_check_origin; - extern m::hookfn conform_check_size; - extern m::hookfn conform_report; -} - -/// Check if event_id is sufficient for the room version. -decltype(ircd::m::vm::conform_check_event_id) -ircd::m::vm::conform_check_event_id -{ - { - { "_site", "vm.conform" } - }, - [](const m::event &event, eval &eval) - { - // Don't care about EDU's on this hook - if(!event.event_id) - return; - - // Conditions for when we don't care if the event_id conforms. This - // hook only cares if the event_id is sufficient for the version, and - // we don't care about the early matrix versions with mxids here. - const bool unaffected - { - !eval.room_version - || eval.room_version == "0" - || eval.room_version == "1" - || eval.room_version == "2" - }; - - if(eval.room_version == "3") - if(!event::id::v3::is(event.event_id)) - throw error - { - fault::INVALID, "Event ID %s is not sufficient for version 3 room.", - string_view{event.event_id} - }; - - // note: we check v4 format for all other room versions, including "4" - if(!unaffected && eval.room_version != "3") - if(!event::id::v4::is(event.event_id)) - throw error - { - fault::INVALID, "Event ID %s in a version %s room is not a version 4 Event ID.", - string_view{event.event_id}, - eval.room_version, - }; - } -}; - -/// Check if an eval with a copts structure (indicating this server is -/// creating the event) has an origin set to !my_host(). -decltype(ircd::m::vm::conform_check_origin) -ircd::m::vm::conform_check_origin -{ - { - { "_site", "vm.conform" } - }, - [](const m::event &event, eval &eval) - { - if(eval.opts && !eval.opts->conforming) - return; - - if(unlikely(eval.copts && !my_host(at<"origin"_>(event)))) - throw error - { - fault::INVALID, "Issuing event for origin: %s", at<"origin"_>(event) - }; - } -}; - -/// Check if an event originating from this server exceeds maximum size. -decltype(ircd::m::vm::conform_check_size) -ircd::m::vm::conform_check_size -{ - { - { "_site", "vm.conform" }, - }, - [](const m::event &event, eval &eval) - { - const size_t &event_size - { - serialized(event) - }; - - if(event_size > size_t(event::max_size)) - throw m::BAD_JSON - { - "Event is %zu bytes which is larger than the maximum %zu bytes", - event_size, - size_t(event::max_size) - }; - } -}; - -/// Generate the conformity report and place the result into the eval. This -/// hook may do some IO to find out if an event is the target of a redaction. -decltype(ircd::m::vm::conform_report) -ircd::m::vm::conform_report -{ - { - { "_site", "vm.conform" } - }, - [](const m::event &event, eval &eval) - { - assert(eval.opts); - const auto &opts(*eval.opts); - - // When opts.conformed is set the report is already generated - if(opts.conformed) - { - eval.report = opts.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, non_conform.report - }; - - // When opts.conforming is false a bad report is not an error. - if(!opts.conforming) - return; - - const bool allow_redaction - { - // allowed by origin server - eval.report.has(event::conforms::MISMATCH_HASHES) - && opts.require_content <= 0 - && opts.node_id == json::get<"origin"_>(event)? - true: - - // allowed by my server - eval.room_internal? - true: - - // allowed by options - non_conform.has(event::conforms::MISMATCH_HASHES)? - true: - - // allowed by room auth - event.event_id? - bool(m::redacted(event.event_id)): - - // otherwise deny - false - }; - - auto report - { - eval.report - }; - - // When allowed, this hook won't throw, but the eval.report will - // still indicate MISMATCH_HASHES. - if(allow_redaction) - report.del(event::conforms::MISMATCH_HASHES); - - if(!report.clean()) - throw error - { - fault::INVALID, "Non-conforming event: %s", - string(report) - }; - } -}; - namespace ircd::m { constexpr size_t event_conforms_num{num_of()}; diff --git a/matrix/vm_conforms.cc b/matrix/vm_conforms.cc new file mode 100644 index 000000000..6330bc75c --- /dev/null +++ b/matrix/vm_conforms.cc @@ -0,0 +1,192 @@ +// Matrix Construct +// +// Copyright (C) Matrix Construct Developers, Authors & Contributors +// Copyright (C) 2016-2022 Jason Volk +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice is present in all copies. The +// full license for this software is available in the LICENSE file. + +namespace ircd::m::vm +{ + extern m::hookfn conform_check_event_id; + extern m::hookfn conform_check_origin; + extern m::hookfn conform_check_size; + extern m::hookfn conform_report; +} + +/// Check if event_id is sufficient for the room version. +decltype(ircd::m::vm::conform_check_event_id) +ircd::m::vm::conform_check_event_id +{ + { + { "_site", "vm.conform" } + }, + [](const m::event &event, eval &eval) + { + // Don't care about EDU's on this hook + if(!event.event_id) + return; + + // Conditions for when we don't care if the event_id conforms. This + // hook only cares if the event_id is sufficient for the version, and + // we don't care about the early matrix versions with mxids here. + const bool unaffected + { + !eval.room_version + || eval.room_version == "0" + || eval.room_version == "1" + || eval.room_version == "2" + }; + + if(eval.room_version == "3") + if(!event::id::v3::is(event.event_id)) + throw error + { + fault::INVALID, "Event ID %s is not sufficient for version 3 room.", + string_view{event.event_id} + }; + + // note: we check v4 format for all other room versions, including "4" + if(!unaffected && eval.room_version != "3") + if(!event::id::v4::is(event.event_id)) + throw error + { + fault::INVALID, "Event ID %s in a version %s room is not a version 4 Event ID.", + string_view{event.event_id}, + eval.room_version, + }; + } +}; + +/// Check if an eval with a copts structure (indicating this server is +/// creating the event) has an origin set to !my_host(). +decltype(ircd::m::vm::conform_check_origin) +ircd::m::vm::conform_check_origin +{ + { + { "_site", "vm.conform" } + }, + [](const m::event &event, eval &eval) + { + if(eval.opts && !eval.opts->conforming) + return; + + if(unlikely(eval.copts && !my_host(at<"origin"_>(event)))) + throw error + { + fault::INVALID, "Issuing event for origin: %s", at<"origin"_>(event) + }; + } +}; + +/// Check if an event originating from this server exceeds maximum size. +decltype(ircd::m::vm::conform_check_size) +ircd::m::vm::conform_check_size +{ + { + { "_site", "vm.conform" }, + }, + [](const m::event &event, eval &eval) + { + const size_t &event_size + { + serialized(event) + }; + + if(event_size > size_t(event::max_size)) + throw m::BAD_JSON + { + "Event is %zu bytes which is larger than the maximum %zu bytes", + event_size, + size_t(event::max_size) + }; + } +}; + +/// Generate the conformity report and place the result into the eval. This +/// hook may do some IO to find out if an event is the target of a redaction. +decltype(ircd::m::vm::conform_report) +ircd::m::vm::conform_report +{ + { + { "_site", "vm.conform" } + }, + [](const m::event &event, eval &eval) + { + assert(eval.opts); + const auto &opts(*eval.opts); + + // When opts.conformed is set the report is already generated + if(opts.conformed) + { + eval.report = opts.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, non_conform.report + }; + + // When opts.conforming is false a bad report is not an error. + if(!opts.conforming) + return; + + const bool allow_redaction + { + // allowed by origin server + eval.report.has(event::conforms::MISMATCH_HASHES) + && opts.require_content <= 0 + && opts.node_id == json::get<"origin"_>(event)? + true: + + // allowed by my server + eval.room_internal? + true: + + // allowed by options + non_conform.has(event::conforms::MISMATCH_HASHES)? + true: + + // allowed by room auth + event.event_id? + bool(m::redacted(event.event_id)): + + // otherwise deny + false + }; + + auto report + { + eval.report + }; + + // When allowed, this hook won't throw, but the eval.report will + // still indicate MISMATCH_HASHES. + if(allow_redaction) + report.del(event::conforms::MISMATCH_HASHES); + + if(!report.clean()) + throw error + { + fault::INVALID, "Non-conforming event: %s", + string(report) + }; + } +};