From c37ccf4df9a56260dd30fb70521ea555b38989c8 Mon Sep 17 00:00:00 2001 From: Pavel Punsky Date: Sat, 18 Apr 2026 17:16:47 -0700 Subject: [PATCH] Pin session origin only after MESSAGE-INTEGRITY validates (#1871) The first ALLOCATE set ss->origin_set=1 before check_stun_auth ran, so an unauthenticated attacker could lock the session into a realm of their choice by forging the ORIGIN attribute on the first packet. If per-realm ACLs differ, this lets the attacker pick the most permissive realm for that session. Defer the commit of ss->origin_set until check_stun_auth succeeds with a valid MESSAGE-INTEGRITY. Until auth passes, every request re-parses ORIGIN, so the 401 challenge still carries the correct realm derived from the current ORIGIN attribute. --- src/server/ns_turn_server.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/server/ns_turn_server.c b/src/server/ns_turn_server.c index 07f6f393..9a353407 100644 --- a/src/server/ns_turn_server.c +++ b/src/server/ns_turn_server.c @@ -3701,8 +3701,10 @@ static int handle_turn_command(turn_turnserver *server, ts_ur_super_session *ss, sar = stun_attr_get_next_str(ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), sar); } - - ss->origin_set = 1; + /* Note: ss->origin_set is intentionally NOT committed here. We pin the origin only + after the request's MESSAGE-INTEGRITY validates (see post-auth block below). An + unauthenticated first ALLOCATE could otherwise lock the session into a realm of + the attacker's choice. Until auth succeeds, every request re-parses the origin. */ } if (!err_code && !(*resp_constructed) && !no_response) { @@ -3716,6 +3718,10 @@ static int handle_turn_command(turn_turnserver *server, ts_ur_super_session *ss, if (postpone_reply) { no_response = 1; } + /* Pin origin only after the request was authenticated (MESSAGE-INTEGRITY validated). */ + if (!err_code && message_integrity && (method == STUN_METHOD_ALLOCATE)) { + ss->origin_set = 1; + } } } }