Skip to content

Commit 2c8802e

Browse files
authored
[core] Validate IPv6 XOR-MAPPED-ADDRESS length in STUN parser (#3115)
`switch_stun_packet_attribute_get_xor_mapped_address()` read and XOR-rewrote a 16-byte IPv6 address whenever the `family` byte was 2, without checking the attribute value was that long. Reject `family == 2` values shorter than `sizeof(switch_stun_ipv6_t)` and clear the output address and port. Adds a regression test.
1 parent 7e1eb3c commit 2c8802e

2 files changed

Lines changed: 55 additions & 0 deletions

File tree

src/switch_stun.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,14 @@ SWITCH_DECLARE(uint8_t) switch_stun_packet_attribute_get_xor_mapped_address(swit
434434
if (ip->family == 2) {
435435
uint8_t *v6addr;
436436

437+
if (attribute->length < sizeof(switch_stun_ipv6_t)) {
438+
/* attribute value too short to hold an IPv6 address; leave outputs defined */
439+
*ipstr = 0;
440+
*port = 0;
441+
442+
return 0;
443+
}
444+
437445
ipv6 = (switch_stun_ipv6_t *)attribute->value;
438446
v6addr = (uint8_t *) &ipv6->address;
439447
v6_xor(v6addr, (uint8_t *)header->id);

tests/unit/switch_stun.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,53 @@ FST_TEARDOWN_END()
106106
}
107107
FST_TEST_END()
108108

109+
FST_TEST_BEGIN(test_stun_get_xor_mapped_address_short_ipv6)
110+
{
111+
/*
112+
* An XOR-MAPPED-ADDRESS attribute whose family byte claims IPv6 (2)
113+
* but whose value is only the 8-byte IPv4 size must be rejected:
114+
* switch_stun_packet_attribute_get_xor_mapped_address must not read
115+
* or XOR a 20-byte IPv6 address out of the 8-byte value. The packet
116+
* is routed through switch_stun_packet_parse first so the attribute
117+
* length is in host byte order, as the live callers see it.
118+
*/
119+
uint8_t buf[512] = { 0 };
120+
switch_stun_packet_t *packet;
121+
switch_stun_packet_attribute_t *attr;
122+
char out_ip[64];
123+
uint16_t out_port = 0xffff;
124+
uint8_t ret;
125+
126+
packet = switch_stun_packet_build_header(SWITCH_STUN_BINDING_RESPONSE, NULL, buf);
127+
128+
/* Value layout: wasted(1) + family(1) + port(2) + address(4) = 8 bytes. */
129+
attr = (switch_stun_packet_attribute_t *)packet->first_attribute;
130+
attr->type = htons(SWITCH_STUN_ATTR_XOR_MAPPED_ADDRESS);
131+
attr->length = htons(8);
132+
attr->value[0] = 0; /* wasted */
133+
attr->value[1] = 2; /* family byte claims IPv6 */
134+
attr->value[2] = 0;
135+
attr->value[3] = 0;
136+
attr->value[4] = 0x01;
137+
attr->value[5] = 0x02;
138+
attr->value[6] = 0x03;
139+
attr->value[7] = 0x04;
140+
packet->header.length = htons(4 + 8);
141+
142+
packet = switch_stun_packet_parse(buf, 20 + 4 + 8);
143+
fst_requires(packet != NULL);
144+
145+
attr = (switch_stun_packet_attribute_t *)packet->first_attribute;
146+
fst_xcheck(attr->length == 8, "parse accepts the 8-byte family-2 XOR-MAPPED-ADDRESS attribute");
147+
148+
memset(out_ip, 'x', sizeof(out_ip));
149+
ret = switch_stun_packet_attribute_get_xor_mapped_address(attr, &packet->header, out_ip, sizeof(out_ip), &out_port);
150+
fst_xcheck(ret == 0, "short IPv6 XOR-MAPPED-ADDRESS attribute is rejected");
151+
fst_xcheck(out_ip[0] == '\0', "output address string left empty on reject");
152+
fst_xcheck(out_port == 0, "output port left defined on reject");
153+
}
154+
FST_TEST_END()
155+
109156
FST_TEST_BEGIN(test_stun_add_binded_address_ipv4)
110157
{
111158
/*

0 commit comments

Comments
 (0)