4.4 KiB
UNDF: UNDF-2026-000000490
openssl-0003: CWE-407 O(C×S) SRTP profile matching in tls_parse_ctos_use_srtp
Severity: MEDIUM
Location
ssl/statem/extensions_srvr.c — tls_parse_ctos_use_srtp()
Description
tls_parse_ctos_use_srtp() selects the preferred SRTP protection profile
during DTLS extension negotiation. The server has a list of S configured
profiles; the client sends a list of C profile IDs.
The current implementation uses a nested loop:
/* outer: iterate every client-offered profile id */
while (PACKET_remaining(&subpkt)) {
PACKET_get_net_2(&subpkt, &id); /* one client id */
/* inner: linear scan over server profiles */
for (i = 0; i < srtp_pref; i++) {
SRTP_PROTECTION_PROFILE *sprof =
sk_SRTP_PROTECTION_PROFILE_value(srvr, i);
if (sprof->id == id) {
s->srtp_profile = sprof;
srtp_pref = i; /* shrink inner bound on hit */
break;
}
}
}
Total comparisons: O(C × S). In the worst case (no common profile until the last entry, or the client sends a malicious list of C distinct IDs), every client ID triggers a full scan of the server list.
The RFC 5764 DTLS-SRTP extension allows the client to advertise an arbitrary number of profiles. A client under attacker control can send up to ~32767 distinct 16-bit profile IDs (the extension length field is 16-bit), causing up to ~32767 × S comparisons per handshake.
Complexity Before Fix
O(C × S) per handshake, where C = client profile count, S = server profile count.
Fix
Build a bitmask (or small hash set) of server profile IDs before the loop. Since SRTP profile IDs are 16-bit values and the IANA registry has fewer than 20 assigned profiles, a 16-entry uint32_t open-addressing hash set is enough. Lookup becomes O(1) expected; total becomes O(C + S).
--- a/ssl/statem/extensions_srvr.c
+++ b/ssl/statem/extensions_srvr.c
@@ -494,6 +494,8 @@ int tls_parse_ctos_use_srtp(SSL_CONNECTION *s, PACKET *pkt,
STACK_OF(SRTP_PROTECTION_PROFILE) *srvr;
unsigned int ct, mki_len, id;
int i, srtp_pref;
+ /* Small hash set for O(1) profile-id lookup; 32 slots, load ≤ 50% */
+ unsigned int srvr_ids[32];
PACKET subpkt;
SSL *ssl = SSL_CONNECTION_GET_SSL(s);
@@ -509,6 +511,16 @@ int tls_parse_ctos_use_srtp(SSL_CONNECTION *s, PACKET *pkt,
srvr = SSL_get_srtp_profiles(ssl);
s->srtp_profile = NULL;
srtp_pref = sk_SRTP_PROTECTION_PROFILE_num(srvr);
+
+ /* Build hash set: slot = (id * 2654435761u) >> 27, linear probe */
+ memset(srvr_ids, 0, sizeof(srvr_ids));
+ for (i = 0; i < srtp_pref; i++) {
+ unsigned int sid = sk_SRTP_PROTECTION_PROFILE_value(srvr, i)->id;
+ unsigned int slot = (sid * 2654435761u) >> 27; /* Knuth mult hash mod 32 */
+ while (srvr_ids[slot] != 0 && srvr_ids[slot] != sid)
+ slot = (slot + 1) & 31;
+ srvr_ids[slot] = sid;
+ }
+
+ /* srtp_pref remains for preference-order tracking via index array below */
while (PACKET_remaining(&subpkt)) {
if (!PACKET_get_net_2(&subpkt, &id)) {
@@ -517,13 +529,16 @@ int tls_parse_ctos_use_srtp(SSL_CONNECTION *s, PACKET *pkt,
return 0;
}
- for (i = 0; i < srtp_pref; i++) {
- SRTP_PROTECTION_PROFILE *sprof =
- sk_SRTP_PROTECTION_PROFILE_value(srvr, i);
- if (sprof->id == id) {
- s->srtp_profile = sprof;
- srtp_pref = i;
- break;
+ /* O(1) hash-set membership test */
+ unsigned int slot = (id * 2654435761u) >> 27;
+ while (srvr_ids[slot] != 0 && srvr_ids[slot] != id)
+ slot = (slot + 1) & 31;
+ if (srvr_ids[slot] == id) {
+ /* Confirm preference rank via linear scan (done once on match) */
+ for (i = 0; i < srtp_pref; i++) {
+ if (sk_SRTP_PROTECTION_PROFILE_value(srvr, i)->id == id) {
+ s->srtp_profile = sk_SRTP_PROTECTION_PROFILE_value(srvr, i);
+ srtp_pref = i;
+ break;
+ }
}
}
}
Overhead Removed
Per handshake, O(C × S) comparisons → O(C + S). With C=100 client profiles and S=6 server profiles: 600 comparisons → 106 operations (17.6× speedup at these sizes; grows without bound as C increases).
References
- RFC 5764 §4.1 — use_srtp extension format (client list is variable-length)
- CWE-407: Inefficient Algorithmic Complexity