diff options
Diffstat (limited to 'content/browser/renderer_host/p2p/socket_host.cc')
-rw-r--r-- | content/browser/renderer_host/p2p/socket_host.cc | 373 |
1 files changed, 373 insertions, 0 deletions
diff --git a/content/browser/renderer_host/p2p/socket_host.cc b/content/browser/renderer_host/p2p/socket_host.cc index 4b45e99..fd1a903 100644 --- a/content/browser/renderer_host/p2p/socket_host.cc +++ b/content/browser/renderer_host/p2p/socket_host.cc @@ -8,13 +8,386 @@ #include "content/browser/renderer_host/p2p/socket_host_tcp.h" #include "content/browser/renderer_host/p2p/socket_host_tcp_server.h" #include "content/browser/renderer_host/p2p/socket_host_udp.h" +#include "crypto/hmac.h" +#include "third_party/libjingle/source/talk/base/asyncpacketsocket.h" +#include "third_party/libjingle/source/talk/base/byteorder.h" +#include "third_party/libjingle/source/talk/base/messagedigest.h" +#include "third_party/libjingle/source/talk/p2p/base/stun.h" namespace { + const uint32 kStunMagicCookie = 0x2112A442; +const int kMinRtpHdrLen = 12; +const int kRtpExtnHdrLen = 4; +const int kDtlsRecordHeaderLen = 13; +const int kTurnChannelHdrLen = 4; +const int kAbsSendTimeExtnLen = 3; +const int kOneByteHdrLen = 1; + +// Fake auth tag written by the render process if external authentication is +// enabled. HMAC in packet will be compared against this value before updating +// packet with actual HMAC value. +static const unsigned char kFakeAuthTag[10] = { + 0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd +}; + +bool IsTurnChannelData(const char* data) { + return ((*data & 0xC0) == 0x40); +} + +bool IsDtlsPacket(const char* data, int len) { + const uint8* u = reinterpret_cast<const uint8*>(data); + return (len >= kDtlsRecordHeaderLen && (u[0] > 19 && u[0] < 64)); +} + +bool IsRtcpPacket(const char* data) { + int type = (static_cast<uint8>(data[1]) & 0x7F); + return (type >= 64 && type < 96); +} + +bool IsTurnSendIndicationPacket(const char* data) { + uint16 type = talk_base::GetBE16(data); + return (type == cricket::TURN_SEND_INDICATION); +} + +bool IsRtpPacket(const char* data, int len) { + return ((*data & 0xC0) == 0x80); +} + +// Verifies rtp header and message length. +bool ValidateRtpHeader(char* rtp, int length) { + int cc_count = rtp[0] & 0x0F; + int rtp_hdr_len_without_extn = kMinRtpHdrLen + 4 * cc_count; + if (rtp_hdr_len_without_extn > length) { + return false; + } + + // If extension bit is not set, we are done with header processing, as input + // length is verified above. + if (!(rtp[0] & 0x10)) { + return true; + } + + rtp += rtp_hdr_len_without_extn; + + // Getting extension profile length. + // Length is in 32 bit words. + uint16 extn_length = talk_base::GetBE16(rtp + 2) * 4; + + // Verify input length against total header size. + if (rtp_hdr_len_without_extn + kRtpExtnHdrLen + extn_length > length) { + return false; + } + return true; +} + +void UpdateAbsSendTimeExtnValue(char* extn_data, int len, + uint32 abs_send_time) { + // Absolute send time in RTP streams. + // + // The absolute send time is signaled to the receiver in-band using the + // general mechanism for RTP header extensions [RFC5285]. The payload + // of this extension (the transmitted value) is a 24-bit unsigned integer + // containing the sender's current time in seconds as a fixed point number + // with 18 bits fractional part. + // + // The form of the absolute send time extension block: + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | ID | len=2 | absolute send time | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + DCHECK_EQ(len, kAbsSendTimeExtnLen); + // Now() has resolution ~1-15ms, using HighResNow(). But it is warned not to + // use it unless necessary, as it is expensive than Now(). + uint32 now_second = abs_send_time; + if (!now_second) { + uint64 now_us = + (base::TimeTicks::HighResNow() - base::TimeTicks()).InMicroseconds(); + // Convert second to 24-bit unsigned with 18 bit fractional part + now_second = + ((now_us << 18) / base::Time::kMicrosecondsPerSecond) & 0x00FFFFFF; + } + // TODO(mallinath) - Add SetBE24 to byteorder.h in libjingle. + extn_data[0] = static_cast<uint8>(now_second >> 16); + extn_data[1] = static_cast<uint8>(now_second >> 8); + extn_data[2] = static_cast<uint8>(now_second); +} + +// Assumes |len| is actual packet length + tag length. Updates HMAC at end of +// the RTP packet. +void UpdateRtpAuthTag(char* rtp, int len, + const talk_base::PacketOptions& options) { + // If there is no key, return. + if (options.packet_time_params.srtp_auth_key.empty()) + return; + + size_t tag_length = options.packet_time_params.srtp_auth_tag_len; + char* auth_tag = rtp + (len - tag_length); + + // We should have a fake HMAC value @ auth_tag. + DCHECK_EQ(0, memcmp(auth_tag, kFakeAuthTag, tag_length)); + + crypto::HMAC hmac(crypto::HMAC::SHA1); + if (!hmac.Init(reinterpret_cast<const unsigned char*>( + &options.packet_time_params.srtp_auth_key[0]), + options.packet_time_params.srtp_auth_key.size())) { + NOTREACHED(); + return; + } + + if (hmac.DigestLength() < tag_length) { + NOTREACHED(); + return; + } + + // Copy ROC after end of rtp packet. + memcpy(auth_tag, &options.packet_time_params.srtp_packet_index, 4); + // Authentication of a RTP packet will have RTP packet + ROC size. + int auth_required_length = len - tag_length + 4; + + unsigned char output[64]; + if (!hmac.Sign(base::StringPiece(rtp, auth_required_length), + output, sizeof(output))) { + NOTREACHED(); + return; + } + // Copy HMAC from output to packet. This is required as auth tag length + // may not be equal to the actual HMAC length. + memcpy(auth_tag, output, tag_length); +} + } // namespace namespace content { +namespace packet_processing_helpers { + +bool ApplyPacketOptions(char* data, int length, + const talk_base::PacketOptions& options, + uint32 abs_send_time) { + DCHECK(data != NULL); + DCHECK(length > 0); + // if there is no valid |rtp_sendtime_extension_id| and |srtp_auth_key| in + // PacketOptions, nothing to be updated in this packet. + if (options.packet_time_params.rtp_sendtime_extension_id == -1 && + options.packet_time_params.srtp_auth_key.empty()) { + return true; + } + + DCHECK(!IsDtlsPacket(data, length)); + DCHECK(!IsRtcpPacket(data)); + + // If there is a srtp auth key present then packet must be a RTP packet. + // RTP packet may have been wrapped in a TURN Channel Data or + // TURN send indication. + int rtp_start_pos; + int rtp_length; + if (!GetRtpPacketStartPositionAndLength( + data, length, &rtp_start_pos, &rtp_length)) { + // This method should never return false. + NOTREACHED(); + return false; + } + + // Skip to rtp packet. + char* start = data + rtp_start_pos; + // If packet option has non default value (-1) for sendtime extension id, + // then we should parse the rtp packet to update the timestamp. Otherwise + // just calculate HMAC and update packet with it. + if (options.packet_time_params.rtp_sendtime_extension_id != -1) { + UpdateRtpAbsSendTimeExtn( + start, rtp_length, + options.packet_time_params.rtp_sendtime_extension_id, abs_send_time); + } + + UpdateRtpAuthTag(start, rtp_length, options); + return true; +} + +bool GetRtpPacketStartPositionAndLength( + char* packet, int length, int* rtp_start_pos, int* rtp_packet_length) { + int rtp_begin, rtp_length; + if (IsTurnChannelData(packet)) { + // Turn Channel Message header format. + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Channel Number | Length | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // / Application Data / + // / / + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + if (length < kTurnChannelHdrLen) { + return false; + } + + rtp_begin = kTurnChannelHdrLen; + rtp_length = talk_base::GetBE16(&packet[2]); + if (length < rtp_length + kTurnChannelHdrLen) { + return false; + } + } else if (IsTurnSendIndicationPacket(packet)) { + if (length <= P2PSocketHost::kStunHeaderSize) { + // Message must be greater than 20 bytes, if it's carrying any payload. + return false; + } + // Validate STUN message length. + int stun_msg_len = talk_base::GetBE16(&packet[2]); + if (stun_msg_len + P2PSocketHost::kStunHeaderSize != length) { + return false; + } + + // First skip mandatory stun header which is of 20 bytes. + rtp_begin = P2PSocketHost::kStunHeaderSize; + // Loop through STUN attributes until we find STUN DATA attribute. + char* start = packet + rtp_begin; + bool data_attr_present = false; + while ((packet + rtp_begin) - start < length) { + // Keep reading STUN attributes until we hit DATA attribute. + // Attribute will be a TLV structure. + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Type | Length | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Value (variable) .... + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // The value in the length field MUST contain the length of the Value + // part of the attribute, prior to padding, measured in bytes. Since + // STUN aligns attributes on 32-bit boundaries, attributes whose content + // is not a multiple of 4 bytes are padded with 1, 2, or 3 bytes of + // padding so that its value contains a multiple of 4 bytes. The + // padding bits are ignored, and may be any value. + uint16 attr_type, attr_length; + // Getting attribute type and length. + attr_type = talk_base::GetBE16(&packet[rtp_begin]); + attr_length = talk_base::GetBE16( + &packet[rtp_begin + sizeof(attr_type)]); + // Checking for bogus attribute length. + if (length < attr_length + rtp_begin) { + return false; + } + + if (attr_type != cricket::STUN_ATTR_DATA) { + rtp_begin += sizeof(attr_type) + sizeof(attr_length) + attr_length; + if ((attr_length % 4) != 0) { + rtp_begin += (4 - (attr_length % 4)); + } + continue; + } + + data_attr_present = true; + rtp_begin += 4; // Skip STUN_DATA_ATTR header. + rtp_length = attr_length; + // One final check of length before exiting. + if (length < rtp_length + rtp_begin) { + return false; + } + // We found STUN_DATA_ATTR. We can skip parsing rest of the packet. + break; + } + + if (!data_attr_present) { + // There is no data attribute present in the message. We can't do anything + // with the message. + return false; + } + + } else { + // This is a raw RTP packet. + rtp_begin = 0; + rtp_length = length; + } + + // Making sure we have a valid RTP packet at the end. + if (IsRtpPacket(packet + rtp_begin, rtp_length) && + ValidateRtpHeader(packet + rtp_begin, rtp_length)) { + *rtp_start_pos = rtp_begin; + *rtp_packet_length = rtp_length; + return true; + } + return false; +} + +// ValidateRtpHeader must be called before this method to make sure, we have +// a sane rtp packet. +bool UpdateRtpAbsSendTimeExtn(char* rtp, int length, + int extension_id, uint32 abs_send_time) { + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // |V=2|P|X| CC |M| PT | sequence number | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | timestamp | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | synchronization source (SSRC) identifier | + // +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + // | contributing source (CSRC) identifiers | + // | .... | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + // Return if extension bit is not set. + if (!(rtp[0] & 0x10)) { + return true; + } + + int cc_count = rtp[0] & 0x0F; + int rtp_hdr_len_without_extn = kMinRtpHdrLen + 4 * cc_count; + + rtp += rtp_hdr_len_without_extn; + + // Getting extension profile ID and length. + uint16 profile_id = talk_base::GetBE16(rtp); + // Length is in 32 bit words. + uint16 extn_length = talk_base::GetBE16(rtp + 2) * 4; + + rtp += kRtpExtnHdrLen; // Moving past extn header. + + bool found = false; + // WebRTC is using one byte header extension. + // TODO(mallinath) - Handle two byte header extension. + if (profile_id == 0xBEDE) { // OneByte extension header + // 0 + // 0 1 2 3 4 5 6 7 + // +-+-+-+-+-+-+-+-+ + // | ID | len | + // +-+-+-+-+-+-+-+-+ + + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | 0xBE | 0xDE | length=3 | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | ID | L=0 | data | ID | L=1 | data... + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ...data | 0 (pad) | 0 (pad) | ID | L=3 | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | data | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + char* extn_start = rtp; + while (rtp - extn_start < extn_length) { + const int id = (*rtp & 0xF0) >> 4; + const int len = (*rtp & 0x0F) + 1; + // The 4-bit length is the number minus one of data bytes of this header + // extension element following the one-byte header. + if (id == extension_id) { + UpdateAbsSendTimeExtnValue(rtp + kOneByteHdrLen, len, abs_send_time); + found = true; + break; + } + rtp += kOneByteHdrLen + len; + // Counting padding bytes. + while ((*rtp == 0) && (rtp - extn_start < extn_length)) { + ++rtp; + } + } + } + return found; +} + +} // packet_processing_helpers + P2PSocketHost::P2PSocketHost(IPC::Sender* message_sender, int id) : message_sender_(message_sender), |