diff --git a/dom/media/webrtc/jsapi/PeerConnectionImpl.cpp b/dom/media/webrtc/jsapi/PeerConnectionImpl.cpp index 5b70a58104..579a620c48 100644 --- a/dom/media/webrtc/jsapi/PeerConnectionImpl.cpp +++ b/dom/media/webrtc/jsapi/PeerConnectionImpl.cpp @@ -116,6 +116,8 @@ #include "mozilla/dom/BrowserChild.h" #include "mozilla/net/WebrtcProxyConfig.h" +#include "MaskConfig.hpp" +#include "mozilla/RustRegex.h" #ifdef XP_WIN // We need to undef the MS macro again in case the windows include file @@ -1705,7 +1707,15 @@ PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP) { } }; - mLocalRequestedSDP = aSDP; + // Sanitize the SDP to prevent IP leaks + std::string sanitizedSDP(aSDP); + nsresult sanitizeResult = SanitizeSDPForIPLeak(sanitizedSDP); + if (NS_FAILED(sanitizeResult)) { + CSFLogError(LOGTAG, "%s - Failed to sanitize SDP", __FUNCTION__); + return sanitizeResult; + } + + mLocalRequestedSDP = sanitizedSDP; SyncToJsep(); @@ -3275,12 +3285,22 @@ const std::string& PeerConnectionImpl::GetName() { return mName; } -void PeerConnectionImpl::CandidateReady(const std::string& candidate, +void PeerConnectionImpl::CandidateReady(const std::string& candidate_, const std::string& transportId, const std::string& ufrag) { STAMP_TIMECARD(mTimeCard, "Ice Candidate gathered"); PC_AUTO_ENTER_API_CALL_VOID_RETURN(false); + const std::string& candidate = [&]() -> const std::string& { + if (ShouldSpoofCandidateIP()) { + static std::string spoofedCandidate; + spoofedCandidate = SpoofCandidateIP(candidate_); + return spoofedCandidate; + } else { + return candidate_; + } + }(); + if (mForceIceTcp && std::string::npos != candidate.find(" UDP ")) { CSFLogWarn(LOGTAG, "Blocking local UDP candidate: %s", candidate.c_str()); STAMP_TIMECARD(mTimeCard, "UDP Ice Candidate blocked"); @@ -3343,10 +3363,89 @@ void PeerConnectionImpl::SendLocalIceCandidateToContent( uint16_t level, const std::string& mid, const std::string& candidate, const std::string& ufrag) { STAMP_TIMECARD(mTimeCard, "Send Ice Candidate to content"); - JSErrorResult rv; - mPCObserver->OnIceCandidate(level, ObString(mid.c_str()), - ObString(candidate.c_str()), - ObString(ufrag.c_str()), rv); + + // Check if IP spoofing is enabled + if (ShouldSpoofCandidateIP()) { + std::string spoofedCandidate = SpoofCandidateIP(candidate); + JSErrorResult rv; + mPCObserver->OnIceCandidate(level, ObString(mid.c_str()), + ObString(spoofedCandidate.c_str()), + ObString(ufrag.c_str()), rv); + } else { + JSErrorResult rv; + mPCObserver->OnIceCandidate(level, ObString(mid.c_str()), + ObString(candidate.c_str()), + ObString(ufrag.c_str()), rv); + } +} + +bool PeerConnectionImpl::ShouldSpoofCandidateIP() const { + // Checks if either webrtc:ipv4 or webrtc:ipv6 is set in the config + return MaskConfig::GetString("webrtc:ipv4").has_value() || + MaskConfig::GetString("webrtc:ipv6").has_value(); +} + +nsresult PeerConnectionImpl::SanitizeSDPForIPLeak(std::string& sdp) { + auto replaceIP = [&sdp](const char* pattern, const char* pref) { + if (auto value = MaskConfig::GetString(pref)) { + mozilla::RustRegex regex(pattern); + if (regex.IsValid()) { + std::string result; + auto iter = regex.IterMatches(sdp); + size_t lastEnd = 0; + while (auto match = iter.Next()) { + result.append(sdp, lastEnd, match->start - lastEnd); + result.append(value.value()); + lastEnd = match->end; + } + result.append(sdp, lastEnd); + sdp = std::move(result); + } + } + }; + + replaceIP("\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b", "webrtc:ipv4"); + replaceIP("\\b(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}\\b", "webrtc:ipv6"); + + return NS_OK; +} + +std::string PeerConnectionImpl::SpoofCandidateIP(const std::string& candidate) { + CSFLogDebug(LOGTAG, "Original candidate: %s", candidate.c_str()); + + std::istringstream iss(candidate); + std::vector tokens{std::istream_iterator{iss}, + std::istream_iterator{}}; + + bool spoofed = false; + // Check for IPv4 or .local address + if (tokens.size() >= 8) { + if (tokens[4].find('.') != std::string::npos) { // IPv4 or .local + if (auto value = MaskConfig::GetString("webrtc:ipv4")) { + tokens[4] = value.value(); + spoofed = true; + } + } + // Check for IPv6 + else if (tokens[4].find(':') != std::string::npos) { + if (auto value = MaskConfig::GetString("webrtc:ipv6")) { + tokens[4] = value.value(); + spoofed = true; + } + } + } + + if (spoofed) { + std::ostringstream oss; + std::copy(tokens.begin(), tokens.end(), + std::ostream_iterator(oss, " ")); + std::string result = oss.str(); + CSFLogDebug(LOGTAG, "Spoofed candidate: %s", result.c_str()); + return result; + } + + CSFLogDebug(LOGTAG, "Candidate not spoofed: %s", candidate.c_str()); + return candidate; } void PeerConnectionImpl::IceConnectionStateChange( @@ -4446,8 +4545,10 @@ bool PeerConnectionImpl::GetPrefDefaultAddressOnly() const { uint64_t winId = mWindow->WindowID(); - bool default_address_only = Preferences::GetBool( - "media.peerconnection.ice.default_address_only", false); + bool default_address_only = + Preferences::GetBool("media.peerconnection.ice.default_address_only", + false) || + ShouldSpoofCandidateIP(); default_address_only |= !MediaManager::Get()->IsActivelyCapturingOrHasAPermission(winId); return default_address_only; diff --git a/dom/media/webrtc/jsapi/PeerConnectionImpl.h b/dom/media/webrtc/jsapi/PeerConnectionImpl.h index d7b54ad721..8d8e92d14b 100644 --- a/dom/media/webrtc/jsapi/PeerConnectionImpl.h +++ b/dom/media/webrtc/jsapi/PeerConnectionImpl.h @@ -624,8 +624,12 @@ class PeerConnectionImpl final RefPtr GetMediaPipelineForTrack( dom::MediaStreamTrack& aRecvTrack); - void CandidateReady(const std::string& candidate, + void CandidateReady(const std::string& candidate_, const std::string& transportId, const std::string& ufrag); + + bool ShouldSpoofCandidateIP() const; + nsresult SanitizeSDPForIPLeak(std::string& sdp); + std::string SpoofCandidateIP(const std::string& candidate); void SendLocalIceCandidateToContent(uint16_t level, const std::string& mid, const std::string& candidate, const std::string& ufrag); diff --git a/dom/media/webrtc/jsapi/moz.build b/dom/media/webrtc/jsapi/moz.build index 05920fc151..34dcf3b9a1 100644 --- a/dom/media/webrtc/jsapi/moz.build +++ b/dom/media/webrtc/jsapi/moz.build @@ -62,3 +62,6 @@ EXPORTS.mozilla.dom += [ ] FINAL_LIBRARY = "xul" + +# DOM Mask +LOCAL_INCLUDES += ["/camoucfg"] \ No newline at end of file