[iOS / libtacky / Jingle] Incoming P2P file offers are auto-accepted and expose the device IP address without user interaction #17

Open
opened 2026-11-04 12:38:42 +00:00 by asemeniv · 1 comment
Member

[iOS / libtacky / Jingle] Incoming P2P file offers are auto-accepted and expose the device IP address without user interaction

Severity: Major
Issue ID: P2P-06, P2P-07
Date: October 26, 2026 · Updated November 4, 2026

Summary

AnotherIM does not use direct peer-to-peer file transfers in its iOS attachment workflow, but leaves libtacky's P2P file transfer functionality enabled with default settings.

As a result, incoming Jingle File Transfer (XEP-0234) offers from contacts can be automatically accepted without user interaction. The client's session-accept response includes a direct connection candidate containing the device's network IP address.

This allows a contact, or someone controlling their account, to obtain the recipient's network address simply by initiating a file transfer.

Depending on the network, the disclosed address may be private or publicly routable.

Testing confirmed that:

  • Incoming P2P file offers are accepted automatically under supported conditions.
  • The recipient sends its network IP address in the Jingle session-accept response.
  • No user confirmation is required.
  • The behavior occurs with both OMEMO-protected JET transfers and unencrypted transfers from contacts without OMEMO.
  • Disabling file_transfer_p2p removes the direct IP candidate in the tested SOCKS5 configuration.

Affected components:

  • iOS: AnotherIM/AppEnvironment.swift
  • libtacky: tacky/xmpp/modules/jinglefile.tcl, s5b.tcl, file.tcl

The issue was reproduced in the libtacky application profile and confirmed against the build 93 source code. Physical-device behavior remains unverified.

Environment

Item Details
Application anotherim-swift c5fdeae, branch feat/ui-redesign, build 93
libtacky 2f42a2f2 / v1.2.0-beta2
Test server ejabberd 26.09 with mod_proxy65, anotherim-server b9e675d
Recipient main@example.local, libtacky application profile
Senders peer2 (OMEMO/JET), observer (without OMEMO)
Recipient IP 192.0.2.2 (test environment)
Transport SOCKS5 / Jingle File Transfer
Verification Level 1 cloud-based test environment

The test environment uses SOCKS5 without WebRTC data channels (MEDIA_BACKEND=none).

Preconditions

  1. Start the test environment:

    PUSH_WINDOW=0 GRACE_PERIOD=0 CLEAN=1 BIND_IP=0.0.0.0 bash setup/up.sh

  2. Build libtacky from the application submodule.

  3. Configure main and peer2 as mutual OMEMO contacts.

  4. Configure main and observer as mutual contacts, with OMEMO unavailable for observer.

  5. Leave file_transfer_p2p and attachment_autofetch unset, matching the iOS application configuration.

Steps to Reproduce

Run:

cd checks && python3 run_p2p.py

P2P-06 — OMEMO/JET transfer

  1. Have peer2 initiate a 3,000-byte direct file transfer to main using Jingle and SOCKS5 with JET (XEP-0391).
  2. Do not perform any action on the recipient.
  3. Inspect the recipient's session-accept stanza and file transfer events.

P2P-07 — Contact without OMEMO

  1. Have main exchange an unencrypted message with observer, which does not support OMEMO.
  2. Have observer initiate a 2,048-byte unencrypted SOCKS5 file transfer.
  3. Do not perform any action on the recipient.
  4. Inspect the recipient's session-accept stanza.

Expected Behavior

Incoming direct file offers should not expose the recipient's network IP address without user interaction.

Since AnotherIM does not expose P2P file transfer functionality in its UI, it should either:

  • Disable incoming direct file transfers; or
  • Restrict transfers to server-proxy connections without advertising the device's direct IP address.

This is a privacy/security recommendation, not an explicitly documented product requirement.

Actual Behavior

P2P-06 — OMEMO/JET

Run 00e86829 (evidence/stanzas_p2p_00e86829.log) showed that main responded to the incoming offer with session-accept in the same second.

The response contained:

<jingle xmlns='urn:xmpp:jingle:1'
        action='session-accept'>
  ...
  <candidate host='192.0.2.2'
             port='37417'
             type='direct'/>
  <candidate host='127.0.0.1'
             port='7777'
             type='proxy'/>
</jingle>

192.0.2.2 was the recipient's network address in the test environment.

The transfer events showed:

  • active immediately after acceptance.
  • done approximately 268 ms later.
  • No offered state requiring user confirmation.

The file was downloaded automatically.

P2P-07 — Unencrypted transfer

In the same run:

  1. main sent an unencrypted message to observer.
  2. observer initiated a SOCKS5 file transfer.
  3. main automatically responded with session-accept.
  4. The response again included host='192.0.2.2' with type='direct'.

No user interaction occurred.

Control tests:

  • Unknown senders did not trigger automatic acceptance under the tested configurations.
  • With file_transfer_p2p 0, the response contained only the proxy candidate (P2P-05).
  • The application's outgoing attachment workflow did not initiate direct P2P transfers (P2P-04).

The XMPP server can also observe the disclosed address because Jingle signaling is transmitted through ordinary XMPP stanzas.

Reproduction Rate

8/8 successful automated runs:

  • October 26: 3ae8f93e, ac7b9bfa, 006606da, 7179ab9c
  • October 27: b1cef65b, ce8a5e88
  • November 3: 00e86829, 63b8406d

Both November 3 runs confirmed automatic acceptance and direct IP disclosure using libtacky v1.2.0-beta2.

Root Cause

libtacky automatically accepts incoming Jingle file offers from eligible contacts.

  1. jinglefile.tcl:1058-1059 calls Accept when the sender passes AutofetchAllowed, the file is within the size limit, and MayAutoAccept permits acceptance.
  2. file.tcl:158-159, 672-687 defaults to autofetching from contacts with a maximum file size of 5 MB.
  3. jinglefile.tcl:1075-1083 allows automatic acceptance for:
    • JET transfers from devices not marked as untrusted.
    • Unencrypted transfers when OMEMO is disabled for the sender.
  4. jinglefile.tcl:2928 includes a direct network candidate unless file_transfer_p2p is explicitly set to "0".
  5. s5b.tcl:240 obtains local addresses from the network connection.

iOS integration:

  • AppEnvironment.swift:396-402 does not configure file_transfer_p2p or attachment_autofetch.
  • The generated TackyWire.File.accept API is not called by the application outside generated code.
  • No direct-file acceptance UI was found.
  • Outgoing attachments use HTTP Upload through AttachmentService.swift:507 and message send_file -url.

Consequently, unused P2P functionality remains enabled in libtacky and can disclose the recipient's address.

Suggested Fix

  1. Disable direct P2P candidates in the iOS libtacky configuration:

    file_transfer_p2p = "0"

    The P2P-05 control test confirmed that this removes the recipient's direct IP candidate from the SOCKS5 response, leaving only the server proxy.

  2. Disable automatic acceptance of incoming direct file transfers, either through a suitable attachment_autofetch configuration or a dedicated libtacky setting.

    Verify that changing autofetch behavior does not interfere with the application's normal HTTP attachment downloads.

  3. Review WebRTC/ICE handling separately. The file_transfer_p2p setting may not prevent IP disclosure through ICE candidates when data channels are available.

Disabling direct candidates alone does not prevent automatic file acceptance.

Verification After Fix

  1. Configure the iOS client with the proposed restrictions.
  2. Initiate an incoming JET file transfer from an OMEMO contact.
  3. Initiate an unencrypted SOCKS5 file transfer from a contact without OMEMO.
  4. Verify that neither scenario exposes a direct device IP address without user approval.
  5. Verify that unsupported incoming transfers are rejected or handled according to the intended application behavior.
  6. Inspect Jingle session-accept responses for direct SOCKS5 and ICE host/srflx candidates.
  7. Repeat on a physical iPhone using Wi-Fi and cellular networks, in foreground and background states.
  8. Confirm that normal HTTP attachment sending and receiving remain functional.

Evidence and Status

Evidence directory:

2026-10-26_P2P_ip_autoaccept/evidence/

November 3 evidence:

  • stanzas_p2p_00e86829.log
  • stanzas_p2p_63b8406d.log
  • stanzas_jingle_00e86829.txt
  • stanzas_jingle_63b8406d.txt
  • events_main_00e86829
  • events_main_63b8406d
  • p2p_00e86829.json
  • p2p_63b8406d.json

Automated test:

autotests/metadata-audit/checks/run_p2p.py

Test report:

autotests/metadata-audit/results/2026-11-03.md

Complete evidence archive:

_claude_tmp/evidence_ma_2026-11-03.tgz

Verified:

  • Automatic acceptance of eligible incoming P2P transfers.
  • Direct network IP disclosure in SOCKS5 Jingle signaling.
  • Reproduction in eight automated runs.
  • Removal of the direct SOCKS5 candidate with file_transfer_p2p 0.
  • Relevant libtacky and iOS integration source code.

Not verified:

  • Behavior on a physical iPhone running TestFlight build 93.
  • Disclosure of a publicly routable address on a real Wi-Fi or cellular network.
  • WebRTC/ICE candidate behavior on iOS.
  • Application UI behavior for automatically received files.
  • Whether the proposed configuration fully prevents IP disclosure across all supported transports.
# [iOS / libtacky / Jingle] Incoming P2P file offers are auto-accepted and expose the device IP address without user interaction **Severity:** Major **Issue ID:** P2P-06, P2P-07 **Date:** October 26, 2026 · Updated November 4, 2026 ## Summary AnotherIM does not use direct peer-to-peer file transfers in its iOS attachment workflow, but leaves libtacky's P2P file transfer functionality enabled with default settings. As a result, incoming Jingle File Transfer (XEP-0234) offers from contacts can be automatically accepted without user interaction. The client's `session-accept` response includes a direct connection candidate containing the device's network IP address. This allows a contact, or someone controlling their account, to obtain the recipient's network address simply by initiating a file transfer. Depending on the network, the disclosed address may be private or publicly routable. **Testing confirmed that:** - Incoming P2P file offers are accepted automatically under supported conditions. - The recipient sends its network IP address in the Jingle `session-accept` response. - No user confirmation is required. - The behavior occurs with both OMEMO-protected JET transfers and unencrypted transfers from contacts without OMEMO. - Disabling `file_transfer_p2p` removes the direct IP candidate in the tested SOCKS5 configuration. **Affected components:** - iOS: `AnotherIM/AppEnvironment.swift` - libtacky: `tacky/xmpp/modules/jinglefile.tcl`, `s5b.tcl`, `file.tcl` The issue was reproduced in the libtacky application profile and confirmed against the build 93 source code. Physical-device behavior remains unverified. ## Environment | Item | Details | |---|---| | Application | anotherim-swift `c5fdeae`, branch `feat/ui-redesign`, build 93 | | libtacky | `2f42a2f2` / v1.2.0-beta2 | | Test server | ejabberd 26.09 with `mod_proxy65`, anotherim-server `b9e675d` | | Recipient | `main@example.local`, libtacky application profile | | Senders | `peer2` (OMEMO/JET), `observer` (without OMEMO) | | Recipient IP | `192.0.2.2` (test environment) | | Transport | SOCKS5 / Jingle File Transfer | | Verification | Level 1 cloud-based test environment | The test environment uses SOCKS5 without WebRTC data channels (`MEDIA_BACKEND=none`). ## Preconditions 1. Start the test environment: `PUSH_WINDOW=0 GRACE_PERIOD=0 CLEAN=1 BIND_IP=0.0.0.0 bash setup/up.sh` 2. Build libtacky from the application submodule. 3. Configure `main` and `peer2` as mutual OMEMO contacts. 4. Configure `main` and `observer` as mutual contacts, with OMEMO unavailable for `observer`. 5. Leave `file_transfer_p2p` and `attachment_autofetch` unset, matching the iOS application configuration. ## Steps to Reproduce Run: `cd checks && python3 run_p2p.py` **P2P-06 — OMEMO/JET transfer** 1. Have `peer2` initiate a 3,000-byte direct file transfer to `main` using Jingle and SOCKS5 with JET (XEP-0391). 2. Do not perform any action on the recipient. 3. Inspect the recipient's `session-accept` stanza and file transfer events. **P2P-07 — Contact without OMEMO** 1. Have `main` exchange an unencrypted message with `observer`, which does not support OMEMO. 2. Have `observer` initiate a 2,048-byte unencrypted SOCKS5 file transfer. 3. Do not perform any action on the recipient. 4. Inspect the recipient's `session-accept` stanza. ## Expected Behavior Incoming direct file offers should not expose the recipient's network IP address without user interaction. Since AnotherIM does not expose P2P file transfer functionality in its UI, it should either: - Disable incoming direct file transfers; or - Restrict transfers to server-proxy connections without advertising the device's direct IP address. This is a privacy/security recommendation, not an explicitly documented product requirement. ## Actual Behavior **P2P-06 — OMEMO/JET** Run `00e86829` (`evidence/stanzas_p2p_00e86829.log`) showed that `main` responded to the incoming offer with `session-accept` in the same second. The response contained: ```xml <jingle xmlns='urn:xmpp:jingle:1' action='session-accept'> ... <candidate host='192.0.2.2' port='37417' type='direct'/> <candidate host='127.0.0.1' port='7777' type='proxy'/> </jingle> ``` `192.0.2.2` was the recipient's network address in the test environment. The transfer events showed: - `active` immediately after acceptance. - `done` approximately 268 ms later. - No `offered` state requiring user confirmation. The file was downloaded automatically. **P2P-07 — Unencrypted transfer** In the same run: 1. `main` sent an unencrypted message to `observer`. 2. `observer` initiated a SOCKS5 file transfer. 3. `main` automatically responded with `session-accept`. 4. The response again included `host='192.0.2.2'` with `type='direct'`. No user interaction occurred. **Control tests:** - Unknown senders did not trigger automatic acceptance under the tested configurations. - With `file_transfer_p2p 0`, the response contained only the proxy candidate (P2P-05). - The application's outgoing attachment workflow did not initiate direct P2P transfers (P2P-04). The XMPP server can also observe the disclosed address because Jingle signaling is transmitted through ordinary XMPP stanzas. ## Reproduction Rate **8/8 successful automated runs:** - October 26: `3ae8f93e`, `ac7b9bfa`, `006606da`, `7179ab9c` - October 27: `b1cef65b`, `ce8a5e88` - November 3: `00e86829`, `63b8406d` Both November 3 runs confirmed automatic acceptance and direct IP disclosure using libtacky v1.2.0-beta2. ## Root Cause libtacky automatically accepts incoming Jingle file offers from eligible contacts. 1. `jinglefile.tcl:1058-1059` calls `Accept` when the sender passes `AutofetchAllowed`, the file is within the size limit, and `MayAutoAccept` permits acceptance. 2. `file.tcl:158-159, 672-687` defaults to autofetching from contacts with a maximum file size of 5 MB. 3. `jinglefile.tcl:1075-1083` allows automatic acceptance for: - JET transfers from devices not marked as untrusted. - Unencrypted transfers when OMEMO is disabled for the sender. 4. `jinglefile.tcl:2928` includes a direct network candidate unless `file_transfer_p2p` is explicitly set to `"0"`. 5. `s5b.tcl:240` obtains local addresses from the network connection. **iOS integration:** - `AppEnvironment.swift:396-402` does not configure `file_transfer_p2p` or `attachment_autofetch`. - The generated `TackyWire.File.accept` API is not called by the application outside generated code. - No direct-file acceptance UI was found. - Outgoing attachments use HTTP Upload through `AttachmentService.swift:507` and `message send_file -url`. Consequently, unused P2P functionality remains enabled in libtacky and can disclose the recipient's address. ## Suggested Fix 1. **Disable direct P2P candidates** in the iOS libtacky configuration: `file_transfer_p2p = "0"` The P2P-05 control test confirmed that this removes the recipient's direct IP candidate from the SOCKS5 response, leaving only the server proxy. 2. **Disable automatic acceptance of incoming direct file transfers**, either through a suitable `attachment_autofetch` configuration or a dedicated libtacky setting. Verify that changing autofetch behavior does not interfere with the application's normal HTTP attachment downloads. 3. **Review WebRTC/ICE handling separately.** The `file_transfer_p2p` setting may not prevent IP disclosure through ICE candidates when data channels are available. Disabling direct candidates alone does not prevent automatic file acceptance. ## Verification After Fix 1. Configure the iOS client with the proposed restrictions. 2. Initiate an incoming JET file transfer from an OMEMO contact. 3. Initiate an unencrypted SOCKS5 file transfer from a contact without OMEMO. 4. Verify that neither scenario exposes a direct device IP address without user approval. 5. Verify that unsupported incoming transfers are rejected or handled according to the intended application behavior. 6. Inspect Jingle `session-accept` responses for direct SOCKS5 and ICE `host`/`srflx` candidates. 7. Repeat on a physical iPhone using Wi-Fi and cellular networks, in foreground and background states. 8. Confirm that normal HTTP attachment sending and receiving remain functional. ## Evidence and Status Evidence directory: `2026-10-26_P2P_ip_autoaccept/evidence/` November 3 evidence: - `stanzas_p2p_00e86829.log` - `stanzas_p2p_63b8406d.log` - `stanzas_jingle_00e86829.txt` - `stanzas_jingle_63b8406d.txt` - `events_main_00e86829` - `events_main_63b8406d` - `p2p_00e86829.json` - `p2p_63b8406d.json` Automated test: `autotests/metadata-audit/checks/run_p2p.py` Test report: `autotests/metadata-audit/results/2026-11-03.md` Complete evidence archive: `_claude_tmp/evidence_ma_2026-11-03.tgz` **Verified:** - Automatic acceptance of eligible incoming P2P transfers. - Direct network IP disclosure in SOCKS5 Jingle signaling. - Reproduction in eight automated runs. - Removal of the direct SOCKS5 candidate with `file_transfer_p2p 0`. - Relevant libtacky and iOS integration source code. **Not verified:** - Behavior on a physical iPhone running TestFlight build 93. - Disclosure of a publicly routable address on a real Wi-Fi or cellular network. - WebRTC/ICE candidate behavior on iOS. - Application UI behavior for automatically received files. - Whether the proposed configuration fully prevents IP disclosure across all supported transports.
Contributor
  • #17: Fixed in 4832765: file_transfer_p2p = 0 and attachment_autofetch_max = 0. libtacky no longer accepts incoming direct offers by itself and offers no direct candidate. The app's HTTP downloads are unaffected. WebRTC/ICE for files was not reviewed.
- #17: Fixed in 4832765: file_transfer_p2p = 0 and attachment_autofetch_max = 0. libtacky no longer accepts incoming direct offers by itself and offers no direct candidate. The app's HTTP downloads are unaffected. WebRTC/ICE for files was not reviewed.
Sign in to join this conversation.
No labels
done
fixed
returned
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
another-im/anotherim-swift#17
No description provided.