From 247c44be307c0bf9c80882019e0788252e508178 Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Fri, 27 Mar 2026 23:57:59 +0100 Subject: [PATCH] fix(xmpp): verify 'by' attribute of stanza-id and MAM result Implement security checks to ensure the 'archive_id' (XEP-0359) used for database deduplication originates from a trusted source. XEP-0359 Section 3.1: "The 'by' attribute MUST be the XMPP address (JID) of the entity assigning the unique and stable stanza ID." Furthermore, Section 4 (Security Considerations) specifies: "A client SHOULD only trust elements from its own server or from a MUC service it is joined to." XEP-0313 Section 4.1.2: "The 'by' attribute of the element is the JID of the archive being queried." and "If the 'by' attribute is not present, the recipient MUST assume that the results are from their own personal archive." Let _handle_chat verify 'by' attribute matches our bare JID. Let _handle_groupchat verify 'by' attribute matches the MUC s bare JID. Let _handle_mam verify the 'by' attribute matches the outer message 'from' (archive JID). If 'by' is missing then 'from' matches our own bare JID (personal archive). Signed-off-by: Michael Vetter --- src/xmpp/message.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/xmpp/message.c b/src/xmpp/message.c index 76179efc..130cace0 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -1067,9 +1067,12 @@ _handle_groupchat(xmpp_stanza_t* const stanza) char* stanzaid = NULL; xmpp_stanza_t* stanzaidst = xmpp_stanza_get_child_by_name_and_ns(stanza, STANZA_NAME_STANZA_ID, STANZA_NS_STABLE_ID); if (stanzaidst) { - stanzaid = (char*)xmpp_stanza_get_attribute(stanzaidst, STANZA_ATTR_ID); - if (stanzaid) { - message->stanzaid = strdup(stanzaid); + const char* by = xmpp_stanza_get_attribute(stanzaidst, "by"); + if (by && (g_strcmp0(by, from_jid->barejid) == 0)) { + stanzaid = (char*)xmpp_stanza_get_attribute(stanzaidst, STANZA_ATTR_ID); + if (stanzaid) { + message->stanzaid = strdup(stanzaid); + } } } @@ -1389,9 +1392,12 @@ _handle_chat(xmpp_stanza_t* const stanza, gboolean is_mam, gboolean is_carbon, c char* stanzaid = NULL; xmpp_stanza_t* stanzaidst = xmpp_stanza_get_child_by_name_and_ns(stanza, STANZA_NAME_STANZA_ID, STANZA_NS_STABLE_ID); if (stanzaidst) { - stanzaid = (char*)xmpp_stanza_get_attribute(stanzaidst, STANZA_ATTR_ID); - if (stanzaid) { - message->stanzaid = strdup(stanzaid); + const char* by = xmpp_stanza_get_attribute(stanzaidst, "by"); + if (by && equals_our_barejid(by)) { + stanzaid = (char*)xmpp_stanza_get_attribute(stanzaidst, STANZA_ATTR_ID); + if (stanzaid) { + message->stanzaid = strdup(stanzaid); + } } } } @@ -1524,6 +1530,20 @@ _handle_mam(xmpp_stanza_t* const stanza) // // same as from XEP-0359 for live messages + const char* by = xmpp_stanza_get_attribute(result, "by"); + const char* from = xmpp_stanza_get_from(stanza); + if (by) { + if (g_strcmp0(by, from) != 0) { + log_warning("MAM result 'by' attribute (%s) does not match 'from' (%s)", by, from); + return TRUE; + } + } else { + if (from && !equals_our_barejid(from)) { + log_warning("MAM result from %s with no 'by' attribute (expected our own JID)", from); + return TRUE; + } + } + const char* result_id = xmpp_stanza_get_id(result); GDateTime* timestamp = stanza_get_delay_from(forwarded, NULL);