Skip to content

Require the peer fingerprint before completing a dtls_gw handshake - #182

Open
arfa-21 wants to merge 1 commit into
sippy:masterfrom
arfa-21:dtls-require-peer-fingerprint
Open

arfa-21 wants to merge 1 commit into
sippy:masterfrom
arfa-21:dtls-require-peer-fingerprint

Conversation

@arfa-21

@arfa-21 arfa-21 commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

Repro: U ... && M4:1 S, then, before the A/P for that leg arrives, send a DTLS ClientHello with any self-signed certificate to the returned port. The handshake completes and SRTP is keyed with that peer. The real A is then refused with "cannot change mode from 0 to 2 when in the 2 state", so relayed media keeps going to (and being accepted from) whoever got there first.
Cause: rtpp_dtls_conn_dtls_recv() answers the handshake in RDC_INIT before any peer fingerprint is set, and the check after the handshake is skipped when pvt->fingerprint is empty.
Fix: drop DTLS packets until A/P has supplied the fingerprint (the peer retransmits its ClientHello), and make the comparison unconditional.

The dtls scenario used by tests/dtls_gw depended on the old behaviour: in the second IPv6 round fwd's gena-facing endpoint is created with S and never gets gena's fingerprint, because the A goes to the geno-facing stream via L6. It now gets it with U6 before gena starts the handshake.

I validated the change by linking the real rtpp_dtls_conn.c/rtpp_dtls.c against an OpenSSL DTLS client. Before the change, a handshake made before A completes and rtp_send protects with its keys. After it, the early ClientHello is ignored and the retransmit completes only once A carries the matching fingerprint; a mismatching fingerprint still fails as before. I could only do this on macOS, where rtpproxy itself doesn't build, so tests/dtls_gw with the adjusted scenario has not been run locally.

An endpoint created by the S command has no remote fingerprint until
the A or P command supplies one, but rtpp_dtls_conn_dtls_recv() still
answered a ClientHello in that state and, once the handshake finished,
skipped the fingerprint check because pvt->fingerprint was empty. Any
host that reached the advertised port between S and A/P could complete
the handshake with a throwaway self-signed certificate: the SRTP
contexts were keyed with that peer, the A/P command that followed was
refused ("cannot change mode ... when in the 2 state") and relayed
media stayed encrypted to, and accepted from, that peer.

Drop DTLS packets while no peer fingerprint is configured, the peer
retransmits its ClientHello and the handshake completes once A/P has
arrived. The check after the handshake no longer has a no-fingerprint
case.

The dtls scenario used by tests/dtls_gw relied on this: in the second
gena's fingerprint (the A went to the geno-facing stream through L6).
Give it gena's fingerprint with U6 before gena starts the handshake.

Validated by driving the real rtpp_dtls_conn object with an OpenSSL
DTLS client: before the change a handshake made before A completes and
keys SRTP, after it the handshake only completes once A has supplied
the matching fingerprint, and a mismatching one still fails.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant