|
| 1 | +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Cheng Zhao <zcbenz@gmail.com> |
| 3 | +Date: Thu, 4 Oct 2018 14:57:02 -0700 |
| 4 | +Subject: fix: prevent pointer from being sent in the clear over SCTP |
| 5 | + |
| 6 | +[1076703] [High] [CVE-2020-6514]: Security: WebRTC: usrsctp is called with pointer as network address |
| 7 | +Backport https://webrtc.googlesource.com/src/+/963cc1ef1336b52ca27742beb28bfbc211ed54d0 |
| 8 | + |
| 9 | +diff --git a/media/sctp/sctp_transport.cc b/media/sctp/sctp_transport.cc |
| 10 | +index 5b631ffcae51726fb739d034a6530cb7be989ade..3f1e3233654bb7ae9285d066c92167f240e1126c 100644 |
| 11 | +--- a/media/sctp/sctp_transport.cc |
| 12 | ++++ b/media/sctp/sctp_transport.cc |
| 13 | +@@ -22,6 +22,7 @@ enum PreservedErrno { |
| 14 | + #include <stdio.h> |
| 15 | + |
| 16 | + #include <memory> |
| 17 | ++#include <unordered_map> |
| 18 | + |
| 19 | + #include "absl/algorithm/container.h" |
| 20 | + #include "absl/types/optional.h" |
| 21 | +@@ -38,6 +39,7 @@ enum PreservedErrno { |
| 22 | + #include "rtc_base/logging.h" |
| 23 | + #include "rtc_base/numerics/safe_conversions.h" |
| 24 | + #include "rtc_base/string_utils.h" |
| 25 | ++#include "rtc_base/thread_annotations.h" |
| 26 | + #include "rtc_base/thread_checker.h" |
| 27 | + #include "rtc_base/trace_event.h" |
| 28 | + #include "usrsctplib/usrsctp.h" |
| 29 | +@@ -71,6 +73,59 @@ enum PayloadProtocolIdentifier { |
| 30 | + PPID_TEXT_LAST = 51 |
| 31 | + }; |
| 32 | + |
| 33 | ++// Maps SCTP transport ID to SctpTransport object, necessary in send threshold |
| 34 | ++// callback and outgoing packet callback. |
| 35 | ++// TODO(crbug.com/1076703): Remove once the underlying problem is fixed or |
| 36 | ++// workaround is provided in usrsctp. |
| 37 | ++class SctpTransportMap { |
| 38 | ++ public: |
| 39 | ++ SctpTransportMap() = default; |
| 40 | ++ |
| 41 | ++ // Assigns a new unused ID to the following transport. |
| 42 | ++ uintptr_t Register(cricket::SctpTransport* transport) { |
| 43 | ++ rtc::CritScope cs(&lock_); |
| 44 | ++ // usrsctp_connect fails with a value of 0... |
| 45 | ++ if (next_id_ == 0) { |
| 46 | ++ ++next_id_; |
| 47 | ++ } |
| 48 | ++ // In case we've wrapped around and need to find an empty spot from a |
| 49 | ++ // removed transport. Assumes we'll never be full. |
| 50 | ++ while (map_.find(next_id_) != map_.end()) { |
| 51 | ++ ++next_id_; |
| 52 | ++ if (next_id_ == 0) { |
| 53 | ++ ++next_id_; |
| 54 | ++ } |
| 55 | ++ }; |
| 56 | ++ map_[next_id_] = transport; |
| 57 | ++ return next_id_++; |
| 58 | ++ } |
| 59 | ++ |
| 60 | ++ // Returns true if found. |
| 61 | ++ bool Deregister(uintptr_t id) { |
| 62 | ++ rtc::CritScope cs(&lock_); |
| 63 | ++ return map_.erase(id) > 0; |
| 64 | ++ } |
| 65 | ++ |
| 66 | ++ cricket::SctpTransport* Retrieve(uintptr_t id) const { |
| 67 | ++ rtc::CritScope cs(&lock_); |
| 68 | ++ auto it = map_.find(id); |
| 69 | ++ if (it == map_.end()) { |
| 70 | ++ return nullptr; |
| 71 | ++ } |
| 72 | ++ return it->second; |
| 73 | ++ } |
| 74 | ++ |
| 75 | ++ private: |
| 76 | ++ rtc::CriticalSection lock_; |
| 77 | ++ |
| 78 | ++ uintptr_t next_id_ RTC_GUARDED_BY(lock_) = 0; |
| 79 | ++ std::unordered_map<uintptr_t, cricket::SctpTransport*> map_ |
| 80 | ++ RTC_GUARDED_BY(lock_); |
| 81 | ++}; |
| 82 | ++ |
| 83 | ++// Should only be modified by UsrSctpWrapper. |
| 84 | ++ABSL_CONST_INIT SctpTransportMap* g_transport_map_ = nullptr; |
| 85 | ++ |
| 86 | + // Helper for logging SCTP messages. |
| 87 | + #if defined(__GNUC__) |
| 88 | + __attribute__((__format__(__printf__, 1, 2))) |
| 89 | +@@ -241,9 +296,12 @@ class SctpTransport::UsrSctpWrapper { |
| 90 | + // Set the number of default outgoing streams. This is the number we'll |
| 91 | + // send in the SCTP INIT message. |
| 92 | + usrsctp_sysctl_set_sctp_nr_outgoing_streams_default(kMaxSctpStreams); |
| 93 | ++ |
| 94 | ++ g_transport_map_ = new SctpTransportMap(); |
| 95 | + } |
| 96 | + |
| 97 | + static void UninitializeUsrSctp() { |
| 98 | ++ delete g_transport_map_; |
| 99 | + RTC_LOG(LS_INFO) << __FUNCTION__; |
| 100 | + // usrsctp_finish() may fail if it's called too soon after the transports |
| 101 | + // are |
| 102 | +@@ -281,7 +339,14 @@ class SctpTransport::UsrSctpWrapper { |
| 103 | + size_t length, |
| 104 | + uint8_t tos, |
| 105 | + uint8_t set_df) { |
| 106 | +- SctpTransport* transport = static_cast<SctpTransport*>(addr); |
| 107 | ++ SctpTransport* transport = |
| 108 | ++ g_transport_map_->Retrieve(reinterpret_cast<uintptr_t>(addr)); |
| 109 | ++ if (!transport) { |
| 110 | ++ RTC_LOG(LS_ERROR) |
| 111 | ++ << "OnSctpOutboundPacket: Failed to get transport for socket ID " |
| 112 | ++ << addr; |
| 113 | ++ return EINVAL; |
| 114 | ++ } |
| 115 | + RTC_LOG(LS_VERBOSE) << "global OnSctpOutboundPacket():" |
| 116 | + << "addr: " << addr << "; length: " << length |
| 117 | + << "; tos: " << rtc::ToHex(tos) |
| 118 | +@@ -390,14 +455,14 @@ class SctpTransport::UsrSctpWrapper { |
| 119 | + return nullptr; |
| 120 | + } |
| 121 | + // usrsctp_getladdrs() returns the addresses bound to this socket, which |
| 122 | +- // contains the SctpTransport* as sconn_addr. Read the pointer, |
| 123 | ++ // contains the SctpTransport id as sconn_addr. Read the id, |
| 124 | + // then free the list of addresses once we have the pointer. We only open |
| 125 | + // AF_CONN sockets, and they should all have the sconn_addr set to the |
| 126 | +- // pointer that created them, so [0] is as good as any other. |
| 127 | ++ // id of the transport that created them, so [0] is as good as any other. |
| 128 | + struct sockaddr_conn* sconn = |
| 129 | + reinterpret_cast<struct sockaddr_conn*>(&addrs[0]); |
| 130 | +- SctpTransport* transport = |
| 131 | +- reinterpret_cast<SctpTransport*>(sconn->sconn_addr); |
| 132 | ++ SctpTransport* transport = g_transport_map_->Retrieve( |
| 133 | ++ reinterpret_cast<uintptr_t>(sconn->sconn_addr)); |
| 134 | + usrsctp_freeladdrs(addrs); |
| 135 | + |
| 136 | + return transport; |
| 137 | +@@ -751,9 +816,10 @@ bool SctpTransport::OpenSctpSocket() { |
| 138 | + UsrSctpWrapper::DecrementUsrSctpUsageCount(); |
| 139 | + return false; |
| 140 | + } |
| 141 | +- // Register this class as an address for usrsctp. This is used by SCTP to |
| 142 | ++ id_ = g_transport_map_->Register(this); |
| 143 | ++ // Register our id as an address for usrsctp. This is used by SCTP to |
| 144 | + // direct the packets received (by the created socket) to this class. |
| 145 | +- usrsctp_register_address(this); |
| 146 | ++ usrsctp_register_address(reinterpret_cast<void*>(id_)); |
| 147 | + return true; |
| 148 | + } |
| 149 | + |
| 150 | +@@ -840,7 +906,8 @@ void SctpTransport::CloseSctpSocket() { |
| 151 | + // discarded instead of being sent. |
| 152 | + usrsctp_close(sock_); |
| 153 | + sock_ = nullptr; |
| 154 | +- usrsctp_deregister_address(this); |
| 155 | ++ usrsctp_deregister_address(reinterpret_cast<void*>(id_)); |
| 156 | ++ RTC_CHECK(g_transport_map_->Deregister(id_)); |
| 157 | + UsrSctpWrapper::DecrementUsrSctpUsageCount(); |
| 158 | + ready_to_send_data_ = false; |
| 159 | + } |
| 160 | +@@ -969,7 +1036,7 @@ void SctpTransport::OnPacketRead(rtc::PacketTransportInternal* transport, |
| 161 | + // will be will be given to the global OnSctpInboundData, and then, |
| 162 | + // marshalled by the AsyncInvoker. |
| 163 | + VerboseLogPacket(data, len, SCTP_DUMP_INBOUND); |
| 164 | +- usrsctp_conninput(this, data, len, 0); |
| 165 | ++ usrsctp_conninput(reinterpret_cast<void*>(id_), data, len, 0); |
| 166 | + } else { |
| 167 | + // TODO(ldixon): Consider caching the packet for very slightly better |
| 168 | + // reliability. |
| 169 | +@@ -995,7 +1062,7 @@ sockaddr_conn SctpTransport::GetSctpSockAddr(int port) { |
| 170 | + #endif |
| 171 | + // Note: conversion from int to uint16_t happens here. |
| 172 | + sconn.sconn_port = rtc::HostToNetwork16(port); |
| 173 | +- sconn.sconn_addr = this; |
| 174 | ++ sconn.sconn_addr = reinterpret_cast<void*>(id_); |
| 175 | + return sconn; |
| 176 | + } |
| 177 | + |
| 178 | +diff --git a/media/sctp/sctp_transport.h b/media/sctp/sctp_transport.h |
| 179 | +index 7337f0103309bbd00d57e805449585ec48720c75..63d8a036deed2057cb367b01418bf1229f851dcb 100644 |
| 180 | +--- a/media/sctp/sctp_transport.h |
| 181 | ++++ b/media/sctp/sctp_transport.h |
| 182 | +@@ -13,6 +13,7 @@ |
| 183 | + |
| 184 | + #include <errno.h> |
| 185 | + |
| 186 | ++#include <cstdint> |
| 187 | + #include <map> |
| 188 | + #include <memory> |
| 189 | + #include <set> |
| 190 | +@@ -266,6 +267,10 @@ class SctpTransport : public SctpTransportInternal, |
| 191 | + absl::optional<int> max_outbound_streams_; |
| 192 | + absl::optional<int> max_inbound_streams_; |
| 193 | + |
| 194 | ++ // Used for associating this transport with the underlying sctp socket in |
| 195 | ++ // various callbacks. |
| 196 | ++ uintptr_t id_ = 0; |
| 197 | ++ |
| 198 | + RTC_DISALLOW_COPY_AND_ASSIGN(SctpTransport); |
| 199 | + }; |
| 200 | + |
0 commit comments