From 3a38d36a60f601b83c25f81fd49407a7cc0ed1b9 Mon Sep 17 00:00:00 2001 From: Matias Fontanini Date: Thu, 6 Nov 2014 21:44:59 -0800 Subject: [PATCH] Fixed invalid parsing of RadioTap ext flag field. --- include/tins/radiotap.h | 103 ++++++++++++++++---------------- src/radiotap.cpp | 128 +++++++++++++++++++++++++--------------- tests/src/radiotap.cpp | 26 +++++++- 3 files changed, 158 insertions(+), 99 deletions(-) diff --git a/include/tins/radiotap.h b/include/tins/radiotap.h index 867d534..eb849e5 100644 --- a/include/tins/radiotap.h +++ b/include/tins/radiotap.h @@ -355,71 +355,74 @@ namespace Tins { */ PDUType pdu_type() const { return PDU::RADIOTAP; } private: + TINS_BEGIN_PACK + #if TINS_IS_LITTLE_ENDIAN + struct flags_type { + uint32_t tsft:1, + flags:1, + rate:1, + channel:1, + fhss:1, + dbm_signal:1, + dbm_noise:1, + lock_quality:1, + tx_attenuation:1, + db_tx_attenuation:1, + dbm_tx_attenuation:1, + antenna:1, + db_signal:1, + db_noise:1, + rx_flags:1, + reserved1:3, + channel_plus:1, + reserved2:12, + ext:1; + } TINS_END_PACK; + #else + struct flags_type { + uint32_t lock_quality:1, + dbm_noise:1, + dbm_signal:1, + fhss:1, + channel:1, + rate:1, + flags:1, + tsft:1, + reserved3:1, + rx_flags:1, + db_tx_attenuation:1, + dbm_tx_attenuation:1, + antenna:1, + db_signal:1, + db_noise:1, + tx_attenuation:1, + reserved2:5, + channel_plus:1, + reserved1:2, + reserved4:7, + ext:1; + } TINS_END_PACK; + #endif + TINS_BEGIN_PACK struct radiotap_hdr { #if TINS_IS_LITTLE_ENDIAN uint8_t it_version; uint8_t it_pad; - uint16_t it_len; - union { - struct { - uint32_t tsft:1, - flags:1, - rate:1, - channel:1, - fhss:1, - dbm_signal:1, - dbm_noise:1, - lock_quality:1, - tx_attenuation:1, - db_tx_attenuation:1, - dbm_tx_attenuation:1, - antenna:1, - db_signal:1, - db_noise:1, - rx_flags:1, - reserved1:3, - channel_plus:1, - reserved2:12, - ext:1; - } flags; - uint32_t flags_32; - }; #else uint8_t it_pad; - uint8_t it_version; + uint8_t it_version; + #endif // TINS_IS_LITTLE_ENDIAN uint16_t it_len; union { - struct { - uint32_t lock_quality:1, - dbm_noise:1, - dbm_signal:1, - fhss:1, - channel:1, - rate:1, - flags:1, - tsft:1, - reserved3:1, - rx_flags:1, - db_tx_attenuation:1, - dbm_tx_attenuation:1, - antenna:1, - db_signal:1, - db_noise:1, - tx_attenuation:1, - reserved2:5, - channel_plus:1, - reserved1:2, - reserved4:7, - ext:1; - } flags; + flags_type flags; uint32_t flags_32; }; - #endif } TINS_END_PACK; void init(); void write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent); + uint32_t find_extra_flag_fields_size(const uint8_t* buffer, uint32_t total_sz); radiotap_hdr _radio; diff --git a/src/radiotap.cpp b/src/radiotap.cpp index cb84a00..7bb1166 100644 --- a/src/radiotap.cpp +++ b/src/radiotap.cpp @@ -77,63 +77,81 @@ RadioTap::RadioTap(const uint8_t *buffer, uint32_t total_sz) std::memcpy(&_radio, buffer, sizeof(_radio)); uint32_t radiotap_hdr_size = length(); check_size(total_sz, radiotap_hdr_size); + // We start on the first flags field, skipping version, pad and length. + const flags_type* current_flags = (const flags_type*)(buffer + sizeof(uint32_t)); + const uint32_t extra_flags_size = find_extra_flag_fields_size( + buffer + sizeof(uint32_t), total_sz); + // Find and skip the extra flag fields. + buffer += extra_flags_size; + radiotap_hdr_size -= extra_flags_size; + // Also skip the header buffer += sizeof(_radio); radiotap_hdr_size -= sizeof(_radio); - if(_radio.flags.tsft) - read_field(buffer, radiotap_hdr_size, _tsft); + while(true) { + _radio.flags_32 |= *(const uint32_t*)current_flags; + if(current_flags->tsft) + read_field(buffer, radiotap_hdr_size, _tsft); + + if(current_flags->flags) + read_field(buffer, radiotap_hdr_size, _flags); + + if(current_flags->rate) + read_field(buffer, radiotap_hdr_size, _rate); - if(_radio.flags.flags) - read_field(buffer, radiotap_hdr_size, _flags); - - if(_radio.flags.rate) - read_field(buffer, radiotap_hdr_size, _rate); - - if(_radio.flags.channel) { - if(((buffer - buffer_start) & 1) == 1) { - buffer++; - radiotap_hdr_size--; + if(current_flags->channel) { + if(((buffer - buffer_start) & 1) == 1) { + buffer++; + radiotap_hdr_size--; + } + read_field(buffer, radiotap_hdr_size, _channel_freq); + read_field(buffer, radiotap_hdr_size, _channel_type); } - read_field(buffer, radiotap_hdr_size, _channel_freq); - read_field(buffer, radiotap_hdr_size, _channel_type); - } - - if(_radio.flags.dbm_signal) - read_field(buffer, radiotap_hdr_size, _dbm_signal); - - if(_radio.flags.dbm_noise) - read_field(buffer, radiotap_hdr_size, _dbm_noise); - - if(_radio.flags.lock_quality) - read_field(buffer, radiotap_hdr_size, _signal_quality); + + if(current_flags->dbm_signal) + read_field(buffer, radiotap_hdr_size, _dbm_signal); + + if(current_flags->dbm_noise) + read_field(buffer, radiotap_hdr_size, _dbm_noise); + + if(current_flags->lock_quality) + read_field(buffer, radiotap_hdr_size, _signal_quality); - if(_radio.flags.antenna) - read_field(buffer, radiotap_hdr_size, _antenna); - - if(_radio.flags.db_signal) - read_field(buffer, radiotap_hdr_size, _db_signal); + if(current_flags->antenna) + read_field(buffer, radiotap_hdr_size, _antenna); + + if(current_flags->db_signal) + read_field(buffer, radiotap_hdr_size, _db_signal); - if(_radio.flags.rx_flags) { - if(((buffer - buffer_start) & 1) == 1) { - buffer++; - radiotap_hdr_size--; + if(current_flags->rx_flags) { + if(((buffer - buffer_start) & 1) == 1) { + buffer++; + radiotap_hdr_size--; + } + read_field(buffer, radiotap_hdr_size, _rx_flags); } - read_field(buffer, radiotap_hdr_size, _rx_flags); - } - if(_radio.flags.channel_plus) { - uint32_t offset = ((buffer - buffer_start) % 4); - if(offset) { - offset = 4 - offset; - buffer += offset; - radiotap_hdr_size -= offset; + if(current_flags->channel_plus) { + uint32_t offset = ((buffer - buffer_start) % 4); + if(offset) { + offset = 4 - offset; + buffer += offset; + radiotap_hdr_size -= offset; + } + uint32_t dummy; + read_field(buffer, radiotap_hdr_size, dummy); + // nasty Big Endian fix + _channel_type = Endian::le_to_host(Endian::host_to_le(dummy)); + read_field(buffer, radiotap_hdr_size, _channel_freq); + read_field(buffer, radiotap_hdr_size, _channel); + read_field(buffer, radiotap_hdr_size, _max_power); + } + // We can do this safely because we checked the size on find_extra_flags... + if(current_flags->ext == 1) { + current_flags++; + } + else { + break; } - uint32_t dummy; - read_field(buffer, radiotap_hdr_size, dummy); - // nasty Big Endian fix - _channel_type = Endian::le_to_host(Endian::host_to_le(dummy)); - read_field(buffer, radiotap_hdr_size, _channel_freq); - read_field(buffer, radiotap_hdr_size, _channel); - read_field(buffer, radiotap_hdr_size, _max_power); } total_sz -= length(); @@ -159,6 +177,20 @@ void RadioTap::init() { antenna(0); } +// This method finds the extra flags field size, taking into account other +// set of flags that may appear if the "ext" bit is on/. +uint32_t RadioTap::find_extra_flag_fields_size(const uint8_t* buffer, uint32_t total_sz) { + const flags_type* ptr = (const flags_type*)buffer; + while (ptr->ext == 1) { + if (total_sz < sizeof(flags_type)) { + throw malformed_packet(); + } + ++ptr; + } + + return (const uint8_t*)ptr - buffer; +} + void RadioTap::version(uint8_t new_version) { _radio.it_version = new_version; } diff --git a/tests/src/radiotap.cpp b/tests/src/radiotap.cpp index 6bf6955..05694ef 100644 --- a/tests/src/radiotap.cpp +++ b/tests/src/radiotap.cpp @@ -8,6 +8,7 @@ #include #include "dot11/dot11_data.h" #include "dot11/dot11_beacon.h" +#include "arp.h" #include "utils.h" using namespace std; @@ -16,7 +17,7 @@ using namespace Tins; class RadioTapTest : public testing::Test { public: static const uint8_t expected_packet[], expected_packet1[], - expected_packet2[]; + expected_packet2[], expected_packet3[]; }; const uint8_t RadioTapTest::expected_packet[] = { @@ -65,6 +66,15 @@ const uint8_t RadioTapTest::expected_packet2[] = { 132, 241, 42, 135, 151, 94, 223, 190, 109, 180, 255, 115, 238, 211 }; +const uint8_t RadioTapTest::expected_packet3[] = { + 0, 0, 36, 0, 47, 64, 0, 160, 32, 8, 0, 0, 0, 0, 0, 0, 75, 136, 126, + 238, 50, 0, 0, 0, 18, 22, 133, 9, 192, 0, 181, 0, 0, 0, 181, 0, 8, + 2, 0, 0, 255, 255, 255, 255, 255, 255, 116, 37, 138, 78, 207, 112, + 0, 102, 75, 134, 135, 47, 32, 84, 170, 170, 3, 0, 0, 0, 8, 6, 0, 1, + 8, 0, 6, 4, 0, 1, 0, 102, 75, 134, 135, 47, 172, 31, 30, 115, 0, 0, + 0, 0, 0, 0, 172, 31, 31, 105, 106, 113, 120, 145 +}; + TEST_F(RadioTapTest, DefaultConstructor) { RadioTap radio; EXPECT_TRUE(radio.flags() & RadioTap::FCS); @@ -129,6 +139,20 @@ TEST_F(RadioTapTest, ConstructorFromBuffer2) { EXPECT_TRUE(radio.find_pdu()); } +TEST_F(RadioTapTest, ConstructorFromBuffer3) { + RadioTap radio(expected_packet3, sizeof(expected_packet3)); + EXPECT_TRUE(radio.present() & RadioTap::RATE); + EXPECT_TRUE(radio.present() & RadioTap::CHANNEL); + EXPECT_TRUE(radio.present() & RadioTap::DBM_SIGNAL); + EXPECT_TRUE(radio.present() & RadioTap::ANTENNA); + EXPECT_TRUE(radio.present() & RadioTap::RX_FLAGS); + + EXPECT_EQ(0, radio.antenna()); + EXPECT_EQ(0xb5, radio.dbm_signal()); + + EXPECT_TRUE(radio.find_pdu()); +} + TEST_F(RadioTapTest, Serialize) { RadioTap radio(expected_packet, sizeof(expected_packet)); RadioTap::serialization_type buffer = radio.serialize();