WIP: security: validate <delay>'s from and <stanza-id>'s by attributes
#97
Draft
jabber.developer2
wants to merge 1 commits from
fix/delay-timestamp-validation into master
pull from: fix/delay-timestamp-validation
merge into: devs:master
devs:master
devs:feat/ai-api-type
devs:fix/editor-terminal-size
devs:ci/docker-hub-publishing
devs:feat/disco-ac
devs:feat/privacy-enhancements
devs:fix/clientid-regression
devs:fix/ai-chat-completions-followup
devs:feat/ai-custom
devs:fix/unencrypted-send
devs:rollback/pre-upstream-merge
devs:fix/autoping-warning-null-domain
devs:fix/pad-dead-space-reclaim
devs:feat/autoping-warning
devs:chore/untrack-gitversion
devs:fix/multiline-pad-clip
devs:fix/issue-112-followups
devs:fix/issue-128-migrate-v3-dedup
devs:ref/light-cleanup
devs:fix/history-scroll-pad-redraw-storm
devs:fix/plugin-post-display-incoming-only
devs:merge/upstream-full
devs:merge-improve
devs:chore/remove-chatlog-stage-1
devs:fix/ai-json-encoding
devs:feat/no-db-backlog-114
devs:fix/scroll-non-chat-windows
devs:fix/paged-non-chat-windows
devs:fix/ai-leaks
devs:feat/ai
devs:fix/ai-followups
devs:test/ai-coverage-unit-only
devs:test/ai-coverage
devs:refactor/scroll-mechanism
devs:fix/verify-per-contact-context
devs:feat/no-db-mode
devs:feat/ai-json
devs:feat/pikaur-parity-arch
devs:fix-pikaur-build
devs:feat/upstream-sync
devs:feat/functest-speedup
devs:fix/cwe-134-format-string-audit
devs:ci/separate-build-step
devs:test/autoping-functional-tests
devs:test/db-functional-tests
devs:fix/arch-build
devs:fix/xep-0030-disco-items-error-handling
devs:fix/xep-0030-empty-disco-items
devs:playground/fix/src_refactoring
devs:tests/disco
devs:fix/test-CI-stability
devs:feat/parallel-tests-clean
devs:feat/parallel-functional-tests
devs:fix/functional_tests_v2
devs:fix/functional_tests
devs:fix/connect_max_args
devs:feat/extended_debug_info
devs:playground/fix/scroll-stuck
devs:build/multicore
devs:build/reenable-fedora
devs:build/autoupdate
No Reviewers
Labels
Clear labels
Compat/Breaking
Breaking change that won't be backward compatible
Kind/Bug
Something is not working
Kind/Documentation
Documentation changes
Kind/Enhancement
Improve existing functionality
Kind/Feature
New functionality
Kind/Security
This is security issue
Kind/Testing
Issue or pull request related to testing
Priority
Critical
1
The priority is critical
Priority
High
2
The priority is high
Priority
Low
4
The priority is low
Priority
Medium
3
The priority is medium
Reviewed
Confirmed
1
Issue has been confirmed
Reviewed
Duplicate
2
This issue or pull request already exists
Reviewed
Invalid
3
Invalid issue
Reviewed
Won't Fix
3
This issue won't be fixed
Status
Abandoned
3
Somebody has started to work on this but abandoned work
Status
Blocked
1
Something is blocking this issue or pull request
Status
Need More Info
2
Feedback is required to reproduce issue or to continue work
No Label
Milestone
No items
No Milestone
Projects
Clear projects
No project
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: devs/cproof#97
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
No description provided.
Delete Branch "fix/delay-timestamp-validation"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Two related security fixes for incoming stanza validation per XEP-0203 §4 and XEP-0359 §5.
Problem
1. Timestamp spoofing via
<delay>(XEP-0203)A remote client can inject a
<delay xmlns='urn:xmpp:delay' stamp='...'>element with an arbitrary timestamp into a message stanza. Profanity accepted any<delay>regardless of itsfromattribute, so a malicious peer could make their message appear at any position in chat history — past or future.XEP-0203 §4 (Security Considerations) states:
2. Archive-ID spoofing via
<stanza-id>(XEP-0359)A remote client can inject a
<stanza-id xmlns='urn:xmpp:sid:0' id='...' by='...'>element with a forged archive ID. Profanity read theidattribute without checkingby, so an attacker could cause MAM deduplication breakage or message omission in history backends that key onarchive_id.XEP-0359 §5 (Security Considerations) states:
Fix
XEP-0203:
<delay>from validationAll three incoming message handlers now use a trust-ordered fallback chain instead of accepting the first
<delay>found:_handle_chatnow_local()_handle_groupchatnow_local()_handle_muc_private_messageNew helper:
stanza_get_oldest_delay_from(stanza, from)— same asstanza_get_oldest_delay()but filters<delay>/<x>children by thefromattribute. The unfiltered version is refactored to delegate withfrom=NULL.The MAM path (
_handle_mam) is deliberately unchanged —<delay>inside<forwarded>comes from our own server's archive and is trusted by construction.XEP-0359:
<stanza-id>by validationbyvalue_handle_groupchat_handle_chatChanged files
stanza_get_oldest_delay_from(), NULL-safety for timestamp comparisonstanza_get_oldest_delay_from(), addSTANZA_ATTR_BYFixes #102
security: validate <delay> from and <stanza-id> by attributesto WIP: security: validate <delay> from and <stanza-id> by attributesWIP: security: validate <delay> from and <stanza-id> by attributesto WIP: security: validate `<delay>`'s `from` and `<stanza-id>`'s `by` attributes5f39dd4939to7fb926c78aUpdate after overlap analysis with PR #105
After reviewing PR #105 (
merge/upstream-full), I noticed it already adds<stanza-id by>validation upstream. To avoid merge conflicts, this PR was reduced to only the parts that don't overlap with #105:Kept (novel to this PR)
<delay from=?>validation in_handle_groupchat,_handle_chat,_handle_muc_private_message— PR #105 still uses unvalidatedstanza_get_delay(stanza)stanza_get_oldest_delay_from()helper + NULL-tmpguard bug-fix instanza_get_oldest_delay()Removed (already in PR #105 or different semantics there)
_handle_groupchat <stanza-id by == room JID>— identical to upstream version in #105_handle_chat <stanza-id by == my_domain>— #105 usesequals_our_barejid(by)instead. We defer to the upstream interpretation rather than fight it; if XEP-0359-strict checking is wanted, that can be a follow-up against the merged upstream codeSTANZA_ATTR_BYdefine — no longer referenced after the above removalsReview fixes applied
g_date_time_new_now_local()fallback in_handle_muc_private_message(was missing, would have lefttimestamp = NULLon the path through)stanza_get_delay_fromsignaturegchar*→const char* const, removed(gchar*)castsMerge order
Land PR #105 first, then this PR will rebase cleanly on the resulting master.
ffc626a276to55c73ee21aRebased on
master; deferring — strict<delay>from-validation regresses history/export timestamps.State of this branch
master(was behind and conflicting) and squashed into one commit.<stanza-id>byhalf of the original PR is dropped:masteralready validatesby, and #130 adds the disco-trust gate on top. What remains is purely the XEP-0203<delay>fromvalidation (stanza_get_oldest_delay_from()+ from-filtered wiring in the three live handlers; MAM<forwarded>stays unfiltered).-Werror, full features); unit tests pass 644/644.Why this is not ready to merge
make checkfails 9 functional tests intest_export_import.c(export_sqlite_to_flatfile,import_flatfile_to_sqlite,bidirectional_merge_to_flatfile,bidirectional_merge_to_sqlite,roundtrip_full_cycle,export_timestamps_preserved,import_timestamps_preserved,export_timestamps_non_utc,muc_export_sqlite_to_flatfile). All of them inject<delay xmlns='urn:xmpp:delay' stamp='…'/>without afromattribute; the new filter rejects anyfrom-less delay, so the stamp is dropped and the message is timestamped "now". These tests are green onmaster, so this is a real regression of the history/export-import feature, not a test artifact.Real-world fragility (beyond the tests)
<delay>fromvaries across servers — bare server domain, account bare JID, or omitted (see the existinggh#1190workaround atmessage.cthat already checks both barejid and domainpart). Exact-match on the server domain loses the timestamp for any of these variants, collapsing the whole offline backlog to receive-time.from=room-bare-JID(handled), but bridges/transports and non-standard rooms may stamp differently → history timestamps lost.<delay>stamped with the bridge's JID matches neither our domain nor the peer's domain → timestamp lost.connection_get_domain()is passed straight tostanza_get_delay_from(); if it returnsNULL, filtering is silently bypassed (any delay accepted).from='<our-domain>'(or omitfrom) to bypass the check — the validation is most meaningful only where a trusted intermediary adds the delay (MUC history, MAM).Suggested path forward
Scope the strict
fromvalidation to the contexts where it is sound — MUC history and MAM<forwarded>(delay added by a trusted intermediary,fromwell-defined). For live 1:1, accept leniently: trust a delay whosefromis absent or in a trusted set (our server domain, our bare JID, the peer's domain, the room JID/domain), and reject only a clearly foreign third-partyfrom. That preserves timestamps in real-world cases while keeping the protection where it actually holds.View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.