Skip to content

Commit

Permalink
Merge pull request #288 from paullouisageneau/fix-turn-server-filtering
Browse files Browse the repository at this point in the history
Fix TURN server permissions enforcement when relaying
  • Loading branch information
paullouisageneau authored Jan 7, 2025
2 parents e5d541d + c98ede7 commit 68a9ea4
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 3 deletions.
30 changes: 29 additions & 1 deletion src/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,21 @@ int server_forward(juice_server_t *server, server_turn_alloc_t *alloc) {
}
addr_unmap_inet6_v4mapped((struct sockaddr *)&record.addr, &record.len);

// RFC 5766 8. Permissions:
// When a UDP datagram arrives at the relayed transport address for the allocation, the
// server extracts the source IP address from the IP header. The server then compares this
// address with the IP address associated with each permission in the list of permissions
// for the allocation. If no match is found, relaying is not permitted, and the server
// silently discards the UDP datagram.
if (!turn_has_permission(&alloc->map, &record)) {
if (JLOG_DEBUG_ENABLED) {
char record_str[ADDR_MAX_STRING_LEN];
addr_record_to_string(&record, record_str, ADDR_MAX_STRING_LEN);
JLOG_DEBUG("No permission for remote address %s, discarding", record_str);
}
return -1;
}

uint16_t channel;
if (turn_get_bound_channel(&alloc->map, &record, &channel)) {
// Use ChannelData
Expand Down Expand Up @@ -1063,12 +1078,21 @@ int server_process_turn_channel_bind(juice_server_t *server, const stun_message_
credentials);
}

// RFC 5766 11.3. Receiving a ChannelBind Response
// When the client receives a ChannelBind success response, it updates its data structures to
// record that the channel binding is now active. It also updates its data structures to record
// that the corresponding permission has been installed or refreshed.
const addr_record_t *peer = msg->peers;
if (!turn_bind_channel(&alloc->map, peer, msg->transaction_id, channel, BIND_LIFETIME)) {
server_answer_stun_error(server, msg->transaction_id, src, msg->msg_method, 500,
credentials);
return -1;
}
if (!turn_set_permission(&alloc->map, msg->transaction_id, peer, PERMISSION_LIFETIME)) {
server_answer_stun_error(server, msg->transaction_id, src, msg->msg_method, 500,
credentials);
return -1;
}

stun_message_t ans;
memset(&ans, 0, sizeof(ans));
Expand Down Expand Up @@ -1105,7 +1129,11 @@ int server_process_turn_send(juice_server_t *server, const stun_message_t *msg,

const addr_record_t *peer = msg->peers;
if (!turn_has_permission(&alloc->map, peer)) {
JLOG_WARN("No permission for peer address");
if (JLOG_WARN_ENABLED) {
char peer_str[ADDR_MAX_STRING_LEN];
addr_record_to_string(peer, peer_str, ADDR_MAX_STRING_LEN);
JLOG_WARN("No permission for peer address %s", peer_str);
}
return -1;
}

Expand Down
22 changes: 20 additions & 2 deletions src/turn.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,14 @@ static void delete_entry(turn_map_t *map, turn_entry_t *entry) {
*/
static turn_entry_t *find_entry(turn_map_t *map, const addr_record_t *record,
turn_entry_type_t type, bool allow_deleted) {
unsigned long key = (addr_record_hash(record, false) + (int)type) % map->map_size;
// RFC 5766: only addresses are compared and port numbers are not considered.
unsigned long key = (addr_record_hash(record, false /*no port*/) + (int)type) % map->map_size;
unsigned long pos = key;
while (true) {
turn_entry_t *entry = map->map + pos;
if (entry->type == TURN_ENTRY_TYPE_EMPTY ||
(entry->type == type && addr_record_is_equal(&entry->record, record, false)))
(entry->type == type &&
addr_record_is_equal(&entry->record, record, false /*no port*/)))
break;

if (allow_deleted && entry->type == TURN_ENTRY_TYPE_DELETED)
Expand Down Expand Up @@ -241,6 +243,13 @@ void turn_destroy_map(turn_map_t *map) {

bool turn_set_permission(turn_map_t *map, const uint8_t *transaction_id,
const addr_record_t *record, timediff_t duration) {
if (record) {
if (JLOG_DEBUG_ENABLED) {
char record_str[ADDR_MAX_STRING_LEN];
addr_record_to_string(record, record_str, ADDR_MAX_STRING_LEN);
JLOG_DEBUG("Updating TURN permission for address %s", record_str);
}
}
return update_timestamp(map, TURN_ENTRY_TYPE_PERMISSION, transaction_id, record, duration);
}

Expand All @@ -254,6 +263,15 @@ bool turn_has_permission(turn_map_t *map, const addr_record_t *record) {

bool turn_bind_channel(turn_map_t *map, const addr_record_t *record, const uint8_t *transaction_id,
uint16_t channel, timediff_t duration) {
if(!record)
return false;

if (JLOG_DEBUG_ENABLED) {
char record_str[ADDR_MAX_STRING_LEN];
addr_record_to_string(record, record_str, ADDR_MAX_STRING_LEN);
JLOG_DEBUG("Binding TURN channel %hu to address %s", channel, record_str);
}

if (!is_valid_channel(channel)) {
JLOG_ERROR("Invalid channel number: 0x%hX", channel);
return false;
Expand Down

0 comments on commit 68a9ea4

Please sign in to comment.