webrtc: sdp: Relax ice-ufrag and ice-pwd checks

According to RFC 8839 section 5.4, if two data streams have identical
"ice-ufrag"s, they MUST have identical "ice-pwd"s.

The previous code wasn't allowing different ice-ufrag values in bundled medias.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9485>
This commit is contained in:
Philippe Normand 2025-08-02 08:55:05 +01:00 committed by GStreamer Marge Bot
parent 9e38ee7526
commit 80e663d560
2 changed files with 84 additions and 14 deletions

View File

@ -322,26 +322,26 @@ validate_sdp (GstWebRTCSignalingState state, SDPSource source,
}
if (!has_session_setup && !_media_has_setup (media, i, error))
goto fail;
/* check parameters in bundle are the same */
/* Validate ICE ufrag and pwd attributes. According to RFC 8839 section 5.4:
* If two data streams have identical "ice-ufrag"s, they MUST have
* identical "ice-pwd"s.
*/
if (media_in_bundle) {
const gchar *ice_ufrag =
gst_sdp_media_get_attribute_val (media, "ice-ufrag");
const gchar *ice_pwd = gst_sdp_media_get_attribute_val (media, "ice-pwd");
if (!bundle_ice_ufrag)
bundle_ice_ufrag = ice_ufrag;
else if (g_strcmp0 (bundle_ice_ufrag, ice_ufrag) != 0) {
g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
"media %u has different ice-ufrag values in bundle. "
"%s != %s", i, bundle_ice_ufrag, ice_ufrag);
goto fail;
}
if (!bundle_ice_pwd) {
bundle_ice_pwd = ice_pwd;
} else if (g_strcmp0 (bundle_ice_pwd, ice_pwd) != 0) {
g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
"media %u has different ice-pwd values in bundle. "
"%s != %s", i, bundle_ice_pwd, ice_pwd);
goto fail;
else if (g_strcmp0 (bundle_ice_ufrag, ice_ufrag) == 0) {
if (!bundle_ice_pwd) {
bundle_ice_pwd = ice_pwd;
} else if (g_strcmp0 (bundle_ice_pwd, ice_pwd) != 0) {
g_set_error (error, GST_WEBRTC_ERROR,
GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
"media %u shares ice-ufrag with another bundled media but has different ice-pwd values. "
"%s != %s", i, bundle_ice_pwd, ice_pwd);
goto fail;
}
}
}
}

View File

@ -6852,6 +6852,75 @@ GST_START_TEST (test_video_rtx_no_duplicate_payloads)
GST_END_TEST;
/* Using different ice-ufrag in bundled medias is allowed as long as they don't share the same ice-pwd. */
GST_START_TEST (test_bundle_with_different_ice_credentials)
{
GstPromise *promise;
struct test_webrtc *t = test_webrtc_new ();
const gchar *sdp_str = "v=0\r\n\
o=- 4962303333179871722 1 IN IP4 0.0.0.0\r\n\
s=-\r\n\
t=0 0\r\n\
a=ice-options:trickle\r\n\
a=group:BUNDLE a1 v1\r\n\
m=audio 10100 UDP/TLS/RTP/SAVPF 96\r\n\
c=IN IP4 0.0.0.0\r\n\
a=mid:a1\r\n\
a=sendrecv\r\n\
a=rtpmap:96 opus/48000/2\r\n\
a=extmap:1 urn:ietf:params:rtp-hdrext:sdes:mid\r\n\
a=extmap:2 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n\
a=msid:47017fee-b6c1-4162-929c-a25110252400 f83006c5-a0ff-4e0a-9ed9-d3e6747be7d9\r\n\
a=ice-ufrag:ETEn\r\n\
a=ice-pwd:OtSK0WpNtpUjkY4+86js7ZQl\r\n\
a=fingerprint:sha-256 19:E2:1C:3B:4B:9F:81:E6:B8:5C:F4:A5:A8:D8:73:04:BB:05:2F:70:9F:04:A9:0E:05:E9:26:33:E8:70:88:A2\r\n\
a=setup:actpass\r\n\
a=rtcp-mux\r\n\
a=rtcp-rsize\r\n\
m=video 10102 UDP/TLS/RTP/SAVPF 100\r\n\
c=IN IP4 0.0.0.0\r\n\
a=mid:v1\r\n\
a=sendrecv\r\n\
a=rtpmap:100 VP8/90000\r\n\
a=extmap:1 urn:ietf:params:rtp-hdrext:sdes:mid\r\n\
a=msid:47017fee-b6c1-4162-929c-a25110252400 f30bdb4a-5db8-49b5-bcdc-e0c9a23172e0\r\n\
a=ice-ufrag:BGKk\r\n\
a=ice-pwd:mqyWsAjvtKwTGnvhPztQ9mIf\r\n\
a=fingerprint:sha-256 19:E2:1C:3B:4B:9F:81:E6:B8:5C:F4:A5:A8:D8:73:04:BB:05:2F:70:9F:04:A9:0E:05:E9:26:33:E8:70:88:A2\r\n\
a=setup:actpass\r\n\
a=rtcp-mux\r\n\
a=rtcp-rsize\r\n";
GstSDPMessage *sdp;
const GstStructure *reply;
t->on_negotiation_needed = NULL;
t->on_offer_created = NULL;
t->on_answer_created = NULL;
gst_sdp_message_new_from_text (sdp_str, &sdp);
GstWebRTCSessionDescription *desc =
gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_OFFER,
sdp);
gst_element_set_state (t->webrtc1, GST_STATE_READY);
promise = gst_promise_new ();
g_signal_emit_by_name (t->webrtc1, "set-remote-description", desc, promise);
gst_promise_wait (promise);
gst_promise_unref (promise);
gst_webrtc_session_description_free (desc);
promise = gst_promise_new ();
g_signal_emit_by_name (t->webrtc1, "create-answer", NULL, promise);
gst_promise_wait (promise);
reply = gst_promise_get_reply (promise);
fail_if (gst_structure_has_field (reply, "error"));
gst_promise_unref (promise);
test_webrtc_free (t);
}
GST_END_TEST;
static Suite *
webrtcbin_suite (void)
{
@ -6957,6 +7026,7 @@ webrtcbin_suite (void)
}
tcase_add_test (tc, test_offer_rollback);
tcase_add_test (tc, test_video_rtx_no_duplicate_payloads);
tcase_add_test (tc, test_bundle_with_different_ice_credentials);
} else {
GST_WARNING ("Some required elements were not found. "
"All media tests are disabled. nicesrc %p, nicesink %p, "