/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "FunctionBroker.h" #include "FunctionBrokerParent.h" #include "PluginQuirks.h" #if defined(XP_WIN) # include # include # include #endif // defined(XP_WIN) using namespace mozilla; using namespace mozilla::ipc; using namespace mozilla::plugins; namespace mozilla::plugins { template static bool CheckQuirks(int aQuirks) { return static_cast(aQuirks & QuirkFlag); } void FreeDestructor(void* aObj) { free(aObj); } #if defined(XP_WIN) // Specialization of EndpointHandlers for Flash file dialog brokering. struct FileDlgEHContainer { template struct EndpointHandler; }; template <> struct FileDlgEHContainer::EndpointHandler : public BaseEndpointHandler> { using BaseEndpointHandler>::Copy; inline static void Copy(OpenFileNameIPC& aDest, const LPOPENFILENAMEW& aSrc) { aDest.CopyFromOfn(aSrc); } inline static void Copy(LPOPENFILENAMEW& aDest, const OpenFileNameRetIPC& aSrc) { aSrc.AddToOfn(aDest); } }; template <> struct FileDlgEHContainer::EndpointHandler : public BaseEndpointHandler> { using BaseEndpointHandler>::Copy; inline static void Copy(OpenFileNameRetIPC& aDest, const LPOPENFILENAMEW& aSrc) { aDest.CopyFromOfn(aSrc); } inline static void Copy(ServerCallData* aScd, LPOPENFILENAMEW& aDest, const OpenFileNameIPC& aSrc) { MOZ_ASSERT(!aDest); ServerCallData::DestructorType* destructor = [](void* aObj) { OpenFileNameIPC::FreeOfnStrings(static_cast(aObj)); DeleteDestructor(aObj); }; aDest = aScd->Allocate(destructor); aSrc.AllocateOfnStrings(aDest); aSrc.AddToOfn(aDest); } }; // FunctionBroker type that uses FileDlgEHContainer template using FileDlgFunctionBroker = FunctionBroker; // Specialization of EndpointHandlers for Flash SSL brokering. struct SslEHContainer { template struct EndpointHandler; }; template <> struct SslEHContainer::EndpointHandler : public BaseEndpointHandler> { using BaseEndpointHandler>::Copy; inline static void Copy(uint64_t& aDest, const PSecHandle& aSrc) { MOZ_ASSERT((aSrc->dwLower == aSrc->dwUpper) && IsOdd(aSrc->dwLower)); aDest = static_cast(aSrc->dwLower); } inline static void Copy(PSecHandle& aDest, const uint64_t& aSrc) { MOZ_ASSERT(IsOdd(aSrc)); aDest->dwLower = static_cast(aSrc); aDest->dwUpper = static_cast(aSrc); } inline static void Copy(IPCSchannelCred& aDest, const PSCHANNEL_CRED& aSrc) { if (aSrc) { aDest.CopyFrom(aSrc); } } inline static void Copy(IPCInternetBuffers& aDest, const LPINTERNET_BUFFERSA& aSrc) { aDest.CopyFrom(aSrc); } }; template <> struct SslEHContainer::EndpointHandler : public BaseEndpointHandler> { using BaseEndpointHandler>::Copy; // PSecHandle is the same thing as PCtxtHandle and PCredHandle. inline static void Copy(uint64_t& aDest, const PSecHandle& aSrc) { // If the SecHandle was an error then don't store it. if (!aSrc) { aDest = 0; return; } static uint64_t sNextVal = 1; UlongPair key(aSrc->dwLower, aSrc->dwUpper); // Fetch val by reference to update the value in the map uint64_t& val = sPairToIdMap[key]; if (val == 0) { MOZ_ASSERT(IsOdd(sNextVal)); val = sNextVal; sIdToPairMap[val] = key; sNextVal += 2; } aDest = val; } // HANDLEs and HINTERNETs marshal with obfuscation (for return values) inline static void Copy(uint64_t& aDest, void* const& aSrc) { // If the HANDLE/HINTERNET was an error then don't store it. if (!aSrc) { aDest = 0; return; } static uint64_t sNextVal = 1; // Fetch val by reference to update the value in the map uint64_t& val = sPtrToIdMap[aSrc]; if (val == 0) { MOZ_ASSERT(IsOdd(sNextVal)); val = sNextVal; sIdToPtrMap[val] = aSrc; sNextVal += 2; } aDest = val; } // HANDLEs and HINTERNETs unmarshal with obfuscation inline static void Copy(void*& aDest, const uint64_t& aSrc) { aDest = nullptr; MOZ_RELEASE_ASSERT(IsOdd(aSrc)); // If the src is not found in the map then we get aDest == 0 void* ptr = sIdToPtrMap[aSrc]; aDest = reinterpret_cast(ptr); MOZ_RELEASE_ASSERT(aDest); } inline static void Copy(PSCHANNEL_CRED& aDest, const IPCSchannelCred& aSrc) { if (aDest) { aSrc.CopyTo(aDest); } } inline static void Copy(ServerCallData* aScd, PSecHandle& aDest, const uint64_t& aSrc) { MOZ_ASSERT(!aDest); MOZ_RELEASE_ASSERT(IsOdd(aSrc)); // If the src is not found in the map then we get the pair { 0, 0 } aDest = aScd->Allocate(); const UlongPair& pair = sIdToPairMap[aSrc]; MOZ_RELEASE_ASSERT(pair.first || pair.second); aDest->dwLower = pair.first; aDest->dwUpper = pair.second; } inline static void Copy(ServerCallData* aScd, PSCHANNEL_CRED& aDest, const IPCSchannelCred& aSrc) { MOZ_ASSERT(!aDest); aDest = aScd->Allocate(); Copy(aDest, aSrc); } inline static void Copy(ServerCallData* aScd, LPINTERNET_BUFFERSA& aDest, const IPCInternetBuffers& aSrc) { MOZ_ASSERT(!aDest); aSrc.CopyTo(aDest); ServerCallData::DestructorType* destructor = [](void* aObj) { LPINTERNET_BUFFERSA inetBuf = static_cast(aObj); IPCInternetBuffers::FreeBuffers(inetBuf); FreeDestructor(inetBuf); }; aScd->PostDestructor(aDest, destructor); } }; // FunctionBroker type that uses SslEHContainer template using SslFunctionBroker = FunctionBroker; /* GetKeyState */ typedef FunctionBroker GetKeyStateFB; template <> ShouldHookFunc* const GetKeyStateFB::BaseType::mShouldHook = &CheckQuirks; /* SetCursorPos */ typedef FunctionBroker SetCursorPosFB; /* GetSaveFileNameW */ typedef FileDlgFunctionBroker GetSaveFileNameWFB; // Remember files granted access in the chrome process static void GrantFileAccess(base::ProcessId aClientId, LPOPENFILENAME& aLpofn, bool isSave) { # if defined(MOZ_SANDBOX) if (aLpofn->Flags & OFN_ALLOWMULTISELECT) { // We only support multiselect with the OFN_EXPLORER flag. // This guarantees that ofn.lpstrFile follows the pattern below. MOZ_ASSERT(aLpofn->Flags & OFN_EXPLORER); // lpstrFile is one of two things: // 1. A null terminated full path to a file, or // 2. A path to a folder, followed by a NULL, followed by a // list of file names, each NULL terminated, followed by an // additional NULL (so it is also double-NULL terminated). std::wstring path = std::wstring(aLpofn->lpstrFile); MOZ_ASSERT(aLpofn->nFileOffset > 0); // For condition #1, nFileOffset points to the file name in the path. // It will be preceeded by a non-NULL character from the path. if (aLpofn->lpstrFile[aLpofn->nFileOffset - 1] != L'\0') { FunctionBrokerParent::GetSandboxPermissions()->GrantFileAccess( aClientId, path.c_str(), isSave); } else { // This is condition #2 wchar_t* nextFile = aLpofn->lpstrFile + path.size() + 1; while (*nextFile != L'\0') { std::wstring nextFileStr(nextFile); std::wstring fullPath = path + std::wstring(L"\\") + nextFileStr; FunctionBrokerParent::GetSandboxPermissions()->GrantFileAccess( aClientId, fullPath.c_str(), isSave); nextFile += nextFileStr.size() + 1; } } } else { FunctionBrokerParent::GetSandboxPermissions()->GrantFileAccess( aClientId, aLpofn->lpstrFile, isSave); } # else MOZ_ASSERT_UNREACHABLE( "GetFileName IPC message is only available on " "Windows builds with sandbox."); # endif } template <> template <> BROKER_DISABLE_CFGUARD BOOL GetSaveFileNameWFB::RunFunction( GetSaveFileNameWFB::FunctionType* aOrigFunction, base::ProcessId aClientId, LPOPENFILENAMEW& aLpofn) const { BOOL result = aOrigFunction(aLpofn); if (result) { // Record any file access permission that was just granted. GrantFileAccess(aClientId, aLpofn, true); } return result; } template <> template <> struct GetSaveFileNameWFB::Response::Info::ShouldMarshal<0> { static const bool value = true; }; /* GetOpenFileNameW */ typedef FileDlgFunctionBroker GetOpenFileNameWFB; template <> template <> BROKER_DISABLE_CFGUARD BOOL GetOpenFileNameWFB::RunFunction( GetOpenFileNameWFB::FunctionType* aOrigFunction, base::ProcessId aClientId, LPOPENFILENAMEW& aLpofn) const { BOOL result = aOrigFunction(aLpofn); if (result) { // Record any file access permission that was just granted. GrantFileAccess(aClientId, aLpofn, false); } return result; } template <> template <> struct GetOpenFileNameWFB::Response::Info::ShouldMarshal<0> { static const bool value = true; }; /* InternetOpenA */ typedef SslFunctionBroker InternetOpenAFB; template <> ShouldHookFunc* const InternetOpenAFB::BaseType::mShouldHook = &CheckQuirks; /* InternetConnectA */ typedef SslFunctionBroker InternetConnectAFB; template <> ShouldHookFunc* const InternetConnectAFB::BaseType::mShouldHook = &CheckQuirks; typedef InternetConnectAFB::Request ICAReqHandler; template <> bool ICAReqHandler::ShouldBroker(Endpoint endpoint, const HINTERNET& h, const LPCSTR& srv, const INTERNET_PORT& port, const LPCSTR& user, const LPCSTR& pass, const DWORD& svc, const DWORD& flags, const DWORD_PTR& cxt) { return (endpoint == SERVER) || IsOdd(reinterpret_cast(h)); } /* InternetCloseHandle */ typedef SslFunctionBroker InternetCloseHandleFB; template <> ShouldHookFunc* const InternetCloseHandleFB::BaseType::mShouldHook = &CheckQuirks; typedef InternetCloseHandleFB::Request ICHReqHandler; template <> bool ICHReqHandler::ShouldBroker(Endpoint endpoint, const HINTERNET& h) { // If we are server side then we were already validated since we had to be // looked up in the "uint64_t <-> HINTERNET" hashtable. // In the client, we check that this is a dummy handle. return (endpoint == SERVER) || IsOdd(reinterpret_cast(h)); } /* InternetQueryDataAvailable */ typedef SslFunctionBroker InternetQueryDataAvailableFB; template <> ShouldHookFunc* const InternetQueryDataAvailableFB::BaseType::mShouldHook = &CheckQuirks; typedef InternetQueryDataAvailableFB::Request IQDAReq; typedef InternetQueryDataAvailableFB::RequestDelegate IQDADelegateReq; template <> void IQDAReq::Marshal(IpdlTuple& aTuple, const HINTERNET& file, const LPDWORD& nBytes, const DWORD& flags, const DWORD_PTR& cxt) { IQDADelegateReq::Marshal(aTuple, file); } template <> bool IQDAReq::Unmarshal(ServerCallData& aScd, const IpdlTuple& aTuple, HINTERNET& file, LPDWORD& nBytes, DWORD& flags, DWORD_PTR& cxt) { bool success = IQDADelegateReq::Unmarshal(aScd, aTuple, file); if (!success) { return false; } flags = 0; cxt = 0; nBytes = aScd.Allocate(); return true; } template <> bool IQDAReq::ShouldBroker(Endpoint endpoint, const HINTERNET& file, const LPDWORD& nBytes, const DWORD& flags, const DWORD_PTR& cxt) { // If we are server side then we were already validated since we had to be // looked up in the "uint64_t <-> HINTERNET" hashtable. // In the client, we check that this is a dummy handle. return (endpoint == SERVER) || ((flags == 0) && (cxt == 0) && IsOdd(reinterpret_cast(file))); } template <> template <> struct InternetQueryDataAvailableFB::Response::Info::ShouldMarshal<1> { static const bool value = true; }; /* InternetReadFile */ typedef SslFunctionBroker InternetReadFileFB; template <> ShouldHookFunc* const InternetReadFileFB::BaseType::mShouldHook = &CheckQuirks; typedef InternetReadFileFB::Request IRFRequestHandler; typedef InternetReadFileFB::RequestDelegate IRFDelegateReq; template <> void IRFRequestHandler::Marshal(IpdlTuple& aTuple, const HINTERNET& h, const LPVOID& buf, const DWORD& nBytesToRead, const LPDWORD& nBytesRead) { IRFDelegateReq::Marshal(aTuple, h, nBytesToRead); } template <> bool IRFRequestHandler::Unmarshal(ServerCallData& aScd, const IpdlTuple& aTuple, HINTERNET& h, LPVOID& buf, DWORD& nBytesToRead, LPDWORD& nBytesRead) { bool ret = IRFDelegateReq::Unmarshal(aScd, aTuple, h, nBytesToRead); if (!ret) { return false; } nBytesRead = aScd.Allocate(); MOZ_ASSERT(nBytesToRead > 0); aScd.AllocateMemory(nBytesToRead, buf); return true; } template <> bool IRFRequestHandler::ShouldBroker(Endpoint endpoint, const HINTERNET& h, const LPVOID& buf, const DWORD& nBytesToRead, const LPDWORD& nBytesRead) { // For server-side validation, the HINTERNET deserialization will have // required it to already be looked up in the IdToPtrMap. At that point, // any call is valid. return (endpoint == SERVER) || IsOdd(reinterpret_cast(h)); } typedef InternetReadFileFB::Response IRFResponseHandler; typedef InternetReadFileFB::ResponseDelegate IRFDelegateResponseHandler; // Marshal the output parameter that we sent to the response delegate. template <> template <> struct IRFResponseHandler::Info::ShouldMarshal<0> { static const bool value = true; }; template <> void IRFResponseHandler::Marshal(IpdlTuple& aTuple, const BOOL& ret, const HINTERNET& h, const LPVOID& buf, const DWORD& nBytesToRead, const LPDWORD& nBytesRead) { nsDependentCSubstring str; if (*nBytesRead) { str.Assign(static_cast(buf), *nBytesRead); } IRFDelegateResponseHandler::Marshal(aTuple, ret, str); } template <> bool IRFResponseHandler::Unmarshal(const IpdlTuple& aTuple, BOOL& ret, HINTERNET& h, LPVOID& buf, DWORD& nBytesToRead, LPDWORD& nBytesRead) { nsDependentCSubstring str; bool success = IRFDelegateResponseHandler::Unmarshal(aTuple, ret, str); if (!success) { return false; } if (str.Length()) { memcpy(buf, str.Data(), str.Length()); *nBytesRead = str.Length(); } return true; } /* InternetWriteFile */ typedef SslFunctionBroker InternetWriteFileFB; template <> ShouldHookFunc* const InternetWriteFileFB::BaseType::mShouldHook = &CheckQuirks; typedef InternetWriteFileFB::Request IWFReqHandler; typedef InternetWriteFileFB::RequestDelegate IWFDelegateReqHandler; template <> void IWFReqHandler::Marshal(IpdlTuple& aTuple, const HINTERNET& file, const LPCVOID& buf, const DWORD& nToWrite, const LPDWORD& nWritten) { MOZ_ASSERT(nWritten); IWFDelegateReqHandler::Marshal( aTuple, file, nsDependentCSubstring(static_cast(buf), nToWrite)); } template <> bool IWFReqHandler::Unmarshal(ServerCallData& aScd, const IpdlTuple& aTuple, HINTERNET& file, LPCVOID& buf, DWORD& nToWrite, LPDWORD& nWritten) { nsDependentCSubstring str; if (!IWFDelegateReqHandler::Unmarshal(aScd, aTuple, file, str)) { return false; } aScd.AllocateString(str, buf, false); nToWrite = str.Length(); nWritten = aScd.Allocate(); return true; } template <> bool IWFReqHandler::ShouldBroker(Endpoint endpoint, const HINTERNET& file, const LPCVOID& buf, const DWORD& nToWrite, const LPDWORD& nWritten) { // For server-side validation, the HINTERNET deserialization will have // required it to already be looked up in the IdToPtrMap. At that point, // any call is valid. return (endpoint == SERVER) || IsOdd(reinterpret_cast(file)); } template <> template <> struct InternetWriteFileFB::Response::Info::ShouldMarshal<3> { static const bool value = true; }; /* InternetSetOptionA */ typedef SslFunctionBroker InternetSetOptionAFB; template <> ShouldHookFunc* const InternetSetOptionAFB::BaseType::mShouldHook = &CheckQuirks; typedef InternetSetOptionAFB::Request ISOAReqHandler; typedef InternetSetOptionAFB::RequestDelegate ISOADelegateReqHandler; template <> void ISOAReqHandler::Marshal(IpdlTuple& aTuple, const HINTERNET& h, const DWORD& opt, const LPVOID& buf, const DWORD& bufLen) { ISOADelegateReqHandler::Marshal( aTuple, h, opt, nsDependentCSubstring(static_cast(buf), bufLen)); } template <> bool ISOAReqHandler::Unmarshal(ServerCallData& aScd, const IpdlTuple& aTuple, HINTERNET& h, DWORD& opt, LPVOID& buf, DWORD& bufLen) { nsDependentCSubstring str; if (!ISOADelegateReqHandler::Unmarshal(aScd, aTuple, h, opt, str)) { return false; } aScd.AllocateString(str, buf, false); bufLen = str.Length(); return true; } template <> bool ISOAReqHandler::ShouldBroker(Endpoint endpoint, const HINTERNET& h, const DWORD& opt, const LPVOID& buf, const DWORD& bufLen) { // For server-side validation, the HINTERNET deserialization will have // required it to already be looked up in the IdToPtrMap. At that point, // any call is valid. return (endpoint == SERVER) || IsOdd(reinterpret_cast(h)); } /* HttpAddRequestHeadersA */ typedef SslFunctionBroker HttpAddRequestHeadersAFB; template <> ShouldHookFunc* const HttpAddRequestHeadersAFB::BaseType::mShouldHook = &CheckQuirks; typedef HttpAddRequestHeadersAFB::Request HARHAReqHandler; template <> bool HARHAReqHandler::ShouldBroker(Endpoint endpoint, const HINTERNET& h, const LPCSTR& head, const DWORD& headLen, const DWORD& mods) { // For server-side validation, the HINTERNET deserialization will have // required it to already be looked up in the IdToPtrMap. At that point, // any call is valid. return (endpoint == SERVER) || IsOdd(reinterpret_cast(h)); } /* HttpOpenRequestA */ typedef SslFunctionBroker HttpOpenRequestAFB; template <> ShouldHookFunc* const HttpOpenRequestAFB::BaseType::mShouldHook = &CheckQuirks; typedef HttpOpenRequestAFB::Request HORAReqHandler; typedef HttpOpenRequestAFB::RequestDelegate, DWORD, DWORD_PTR)> HORADelegateReqHandler; template <> void HORAReqHandler::Marshal(IpdlTuple& aTuple, const HINTERNET& h, const LPCSTR& verb, const LPCSTR& obj, const LPCSTR& ver, const LPCSTR& ref, LPCSTR* const& acceptTypes, const DWORD& flags, const DWORD_PTR& cxt) { CopyableTArray arrayAcceptTypes; LPCSTR* curAcceptType = acceptTypes; if (curAcceptType) { while (*curAcceptType) { arrayAcceptTypes.AppendElement(nsCString(*curAcceptType)); ++curAcceptType; } } // XXX Could we move arrayAcceptTypes here? HORADelegateReqHandler::Marshal(aTuple, h, verb, obj, ver, ref, arrayAcceptTypes, flags, cxt); } template <> bool HORAReqHandler::Unmarshal(ServerCallData& aScd, const IpdlTuple& aTuple, HINTERNET& h, LPCSTR& verb, LPCSTR& obj, LPCSTR& ver, LPCSTR& ref, LPCSTR*& acceptTypes, DWORD& flags, DWORD_PTR& cxt) { CopyableTArray arrayAcceptTypes; if (!HORADelegateReqHandler::Unmarshal(aScd, aTuple, h, verb, obj, ver, ref, arrayAcceptTypes, flags, cxt)) { return false; } if (arrayAcceptTypes.Length() == 0) { acceptTypes = nullptr; } else { aScd.AllocateMemory((arrayAcceptTypes.Length() + 1) * sizeof(LPCSTR), acceptTypes); for (size_t i = 0; i < arrayAcceptTypes.Length(); ++i) { aScd.AllocateString(arrayAcceptTypes[i], acceptTypes[i]); } acceptTypes[arrayAcceptTypes.Length()] = nullptr; } return true; } template <> bool HORAReqHandler::ShouldBroker(Endpoint endpoint, const HINTERNET& h, const LPCSTR& verb, const LPCSTR& obj, const LPCSTR& ver, const LPCSTR& ref, LPCSTR* const& acceptTypes, const DWORD& flags, const DWORD_PTR& cxt) { // For the server-side test, the HINTERNET deserialization will have // required it to already be looked up in the IdToPtrMap. At that point, // any call is valid. return (endpoint == SERVER) || IsOdd(reinterpret_cast(h)); } /* HttpQueryInfoA */ typedef SslFunctionBroker HttpQueryInfoAFB; template <> ShouldHookFunc* const HttpQueryInfoAFB::BaseType::mShouldHook = &CheckQuirks; typedef HttpQueryInfoAFB::Request HQIARequestHandler; typedef HttpQueryInfoAFB::RequestDelegate HQIADelegateRequestHandler; template <> void HQIARequestHandler::Marshal(IpdlTuple& aTuple, const HINTERNET& h, const DWORD& lvl, const LPVOID& buf, const LPDWORD& bufLen, const LPDWORD& idx) { HQIADelegateRequestHandler::Marshal(aTuple, h, lvl, bufLen != nullptr, bufLen ? *bufLen : 0, idx != nullptr, idx ? *idx : 0); } template <> bool HQIARequestHandler::Unmarshal(ServerCallData& aScd, const IpdlTuple& aTuple, HINTERNET& h, DWORD& lvl, LPVOID& buf, LPDWORD& bufLen, LPDWORD& idx) { BOOL hasBufLen, hasIdx; DWORD tempBufLen, tempIdx; bool success = HQIADelegateRequestHandler::Unmarshal( aScd, aTuple, h, lvl, hasBufLen, tempBufLen, hasIdx, tempIdx); if (!success) { return false; } bufLen = nullptr; if (hasBufLen) { aScd.AllocateMemory(tempBufLen, buf, bufLen); } idx = nullptr; if (hasIdx) { idx = aScd.Allocate(tempIdx); } return true; } template <> bool HQIARequestHandler::ShouldBroker(Endpoint endpoint, const HINTERNET& h, const DWORD& lvl, const LPVOID& buf, const LPDWORD& bufLen, const LPDWORD& idx) { return (endpoint == SERVER) || IsOdd(reinterpret_cast(h)); } // Marshal all of the output parameters that we sent to the response delegate. template <> template <> struct HttpQueryInfoAFB::Response::Info::ShouldMarshal<0> { static const bool value = true; }; template <> template <> struct HttpQueryInfoAFB::Response::Info::ShouldMarshal<1> { static const bool value = true; }; template <> template <> struct HttpQueryInfoAFB::Response::Info::ShouldMarshal<2> { static const bool value = true; }; typedef HttpQueryInfoAFB::Response HQIAResponseHandler; typedef HttpQueryInfoAFB::ResponseDelegate HQIADelegateResponseHandler; template <> void HQIAResponseHandler::Marshal(IpdlTuple& aTuple, const BOOL& ret, const HINTERNET& h, const DWORD& lvl, const LPVOID& buf, const LPDWORD& bufLen, const LPDWORD& idx) { nsDependentCSubstring str; if (buf && ret) { MOZ_ASSERT(bufLen); str.Assign(static_cast(buf), *bufLen); } // Note that we send the bufLen separately to handle the case where buf wasn't // allocated or large enough to hold the entire return value. bufLen is then // the required buffer size. HQIADelegateResponseHandler::Marshal(aTuple, ret, str, bufLen ? *bufLen : 0, idx ? *idx : 0); } template <> bool HQIAResponseHandler::Unmarshal(const IpdlTuple& aTuple, BOOL& ret, HINTERNET& h, DWORD& lvl, LPVOID& buf, LPDWORD& bufLen, LPDWORD& idx) { DWORD totalBufLen = *bufLen; nsDependentCSubstring str; DWORD tempBufLen, tempIdx; bool success = HQIADelegateResponseHandler::Unmarshal(aTuple, ret, str, tempBufLen, tempIdx); if (!success) { return false; } if (bufLen) { *bufLen = tempBufLen; } if (idx) { *idx = tempIdx; } if (buf && ret) { // When HttpQueryInfo returns strings, the buffer length will not include // the null terminator. Rather than (brittle-y) trying to determine if the // return buffer is a string, we always tack on a null terminator if the // buffer has room for it. MOZ_ASSERT(str.Length() == *bufLen); memcpy(buf, str.Data(), str.Length()); if (str.Length() < totalBufLen) { char* cbuf = static_cast(buf); cbuf[str.Length()] = '\0'; } } return true; } /* HttpSendRequestA */ typedef SslFunctionBroker HttpSendRequestAFB; template <> ShouldHookFunc* const HttpSendRequestAFB::BaseType::mShouldHook = &CheckQuirks; typedef HttpSendRequestAFB::Request HSRARequestHandler; typedef HttpSendRequestAFB::RequestDelegate HSRADelegateRequestHandler; template <> void HSRARequestHandler::Marshal(IpdlTuple& aTuple, const HINTERNET& h, const LPCSTR& head, const DWORD& headLen, const LPVOID& opt, const DWORD& optLen) { nsDependentCSubstring headStr; headStr.SetIsVoid(head == nullptr); if (head) { // HttpSendRequest allows headLen == -1L for length of a null terminated // string. DWORD ncHeadLen = headLen; if (ncHeadLen == -1L) { ncHeadLen = strlen(head); } headStr.Rebind(head, ncHeadLen); } nsDependentCSubstring optStr; optStr.SetIsVoid(opt == nullptr); if (opt) { optStr.Rebind(static_cast(opt), optLen); } HSRADelegateRequestHandler::Marshal(aTuple, h, headStr, optStr); } template <> bool HSRARequestHandler::Unmarshal(ServerCallData& aScd, const IpdlTuple& aTuple, HINTERNET& h, LPCSTR& head, DWORD& headLen, LPVOID& opt, DWORD& optLen) { nsDependentCSubstring headStr; nsDependentCSubstring optStr; bool success = HSRADelegateRequestHandler::Unmarshal(aScd, aTuple, h, headStr, optStr); if (!success) { return false; } if (headStr.IsVoid()) { head = nullptr; MOZ_ASSERT(headLen == 0); } else { aScd.AllocateString(headStr, head, false); headLen = headStr.Length(); } if (optStr.IsVoid()) { opt = nullptr; MOZ_ASSERT(optLen == 0); } else { aScd.AllocateString(optStr, opt, false); optLen = optStr.Length(); } return true; } template <> bool HSRARequestHandler::ShouldBroker(Endpoint endpoint, const HINTERNET& h, const LPCSTR& head, const DWORD& headLen, const LPVOID& opt, const DWORD& optLen) { // If we are server side then we were already validated since we had to be // looked up in the "uint64_t <-> HINTERNET" hashtable. // In the client, we check that this is a dummy handle. return (endpoint == SERVER) || IsOdd(reinterpret_cast(h)); } /* HttpSendRequestExA */ typedef SslFunctionBroker HttpSendRequestExAFB; template <> ShouldHookFunc* const HttpSendRequestExAFB::BaseType::mShouldHook = &CheckQuirks; typedef RequestInfo HSRExAReqInfo; template <> template <> struct HSRExAReqInfo::FixedValue<2> { static const LPINTERNET_BUFFERSA value; }; const LPINTERNET_BUFFERSA HSRExAReqInfo::FixedValue<2>::value = nullptr; // Docs for HttpSendRequestExA say this parameter 'must' be zero but Flash // passes other values. // template<> template<> // struct HSRExAReqInfo::FixedValue<3> { static const DWORD value = 0; }; template <> template <> struct HSRExAReqInfo::FixedValue<4> { static const DWORD_PTR value; }; const DWORD_PTR HSRExAReqInfo::FixedValue<4>::value = 0; /* HttpEndRequestA */ typedef SslFunctionBroker HttpEndRequestAFB; template <> ShouldHookFunc* const HttpEndRequestAFB::BaseType::mShouldHook = &CheckQuirks; typedef RequestInfo HERAReqInfo; template <> template <> struct HERAReqInfo::FixedValue<1> { static const LPINTERNET_BUFFERSA value; }; const LPINTERNET_BUFFERSA HERAReqInfo::FixedValue<1>::value = nullptr; template <> template <> struct HERAReqInfo::FixedValue<2> { static const DWORD value; }; const DWORD HERAReqInfo::FixedValue<2>::value = 0; template <> template <> struct HERAReqInfo::FixedValue<3> { static const DWORD_PTR value; }; const DWORD_PTR HERAReqInfo::FixedValue<3>::value = 0; /* InternetQueryOptionA */ typedef SslFunctionBroker InternetQueryOptionAFB; template <> ShouldHookFunc* const InternetQueryOptionAFB::BaseType::mShouldHook = &CheckQuirks; typedef InternetQueryOptionAFB::Request IQOARequestHandler; typedef InternetQueryOptionAFB::RequestDelegate IQOADelegateRequestHandler; template <> void IQOARequestHandler::Marshal(IpdlTuple& aTuple, const HINTERNET& h, const DWORD& opt, const LPVOID& buf, const LPDWORD& bufLen) { MOZ_ASSERT(bufLen); IQOADelegateRequestHandler::Marshal(aTuple, h, opt, buf ? *bufLen : 0); } template <> bool IQOARequestHandler::Unmarshal(ServerCallData& aScd, const IpdlTuple& aTuple, HINTERNET& h, DWORD& opt, LPVOID& buf, LPDWORD& bufLen) { DWORD tempBufLen; bool success = IQOADelegateRequestHandler::Unmarshal(aScd, aTuple, h, opt, tempBufLen); if (!success) { return false; } aScd.AllocateMemory(tempBufLen, buf, bufLen); return true; } template <> bool IQOARequestHandler::ShouldBroker(Endpoint endpoint, const HINTERNET& h, const DWORD& opt, const LPVOID& buf, const LPDWORD& bufLen) { // If we are server side then we were already validated since we had to be // looked up in the "uint64_t <-> HINTERNET" hashtable. // In the client, we check that this is a dummy handle. return (endpoint == SERVER) || IsOdd(reinterpret_cast(h)); } // Marshal all of the output parameters that we sent to the response delegate. template <> template <> struct InternetQueryOptionAFB::Response::Info::ShouldMarshal<0> { static const bool value = true; }; template <> template <> struct InternetQueryOptionAFB::Response::Info::ShouldMarshal<1> { static const bool value = true; }; typedef InternetQueryOptionAFB::Response IQOAResponseHandler; typedef InternetQueryOptionAFB::ResponseDelegate IQOADelegateResponseHandler; template <> void IQOAResponseHandler::Marshal(IpdlTuple& aTuple, const BOOL& ret, const HINTERNET& h, const DWORD& opt, const LPVOID& buf, const LPDWORD& bufLen) { nsDependentCSubstring str; if (buf && ret) { MOZ_ASSERT(*bufLen); str.Assign(static_cast(buf), *bufLen); } IQOADelegateResponseHandler::Marshal(aTuple, ret, str, *bufLen); } template <> bool IQOAResponseHandler::Unmarshal(const IpdlTuple& aTuple, BOOL& ret, HINTERNET& h, DWORD& opt, LPVOID& buf, LPDWORD& bufLen) { nsDependentCSubstring str; bool success = IQOADelegateResponseHandler::Unmarshal(aTuple, ret, str, *bufLen); if (!success) { return false; } if (buf && ret) { MOZ_ASSERT(str.Length() == *bufLen); memcpy(buf, str.Data(), str.Length()); } return true; } /* InternetErrorDlg */ typedef SslFunctionBroker InternetErrorDlgFB; template <> ShouldHookFunc* const InternetErrorDlgFB::BaseType::mShouldHook = &CheckQuirks; typedef RequestInfo IEDReqInfo; template <> template <> struct IEDReqInfo::FixedValue<4> { static LPVOID* const value; }; LPVOID* const IEDReqInfo::FixedValue<4>::value = nullptr; typedef InternetErrorDlgFB::Request IEDReqHandler; template <> bool IEDReqHandler::ShouldBroker(Endpoint endpoint, const HWND& hwnd, const HINTERNET& h, const DWORD& err, const DWORD& flags, LPVOID* const& data) { const DWORD SUPPORTED_FLAGS = FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA | FLAGS_ERROR_UI_FLAGS_NO_UI; // We broker if (1) the handle h is brokered (odd in client), // (2) we support the requested action flags and (3) there is no user // data, which wouldn't make sense for our supported flags anyway. return ((endpoint == SERVER) || IsOdd(reinterpret_cast(h))) && (!(flags & ~SUPPORTED_FLAGS)) && (data == nullptr); } /* AcquireCredentialsHandleA */ typedef SslFunctionBroker AcquireCredentialsHandleAFB; template <> ShouldHookFunc* const AcquireCredentialsHandleAFB::BaseType::mShouldHook = &CheckQuirks; typedef RequestInfo ACHAReqInfo; template <> template <> struct ACHAReqInfo::FixedValue<0> { static const LPSTR value; }; const LPSTR ACHAReqInfo::FixedValue<0>::value = nullptr; template <> template <> struct ACHAReqInfo::FixedValue<1> { static const LPSTR value; }; const LPSTR ACHAReqInfo::FixedValue<1>::value = const_cast(UNISP_NAME_A); // -Wwritable-strings template <> template <> struct ACHAReqInfo::FixedValue<2> { static const unsigned long value; }; const unsigned long ACHAReqInfo::FixedValue<2>::value = SECPKG_CRED_OUTBOUND; template <> template <> struct ACHAReqInfo::FixedValue<3> { static void* const value; }; void* const ACHAReqInfo::FixedValue<3>::value = nullptr; template <> template <> struct ACHAReqInfo::FixedValue<5> { static const SEC_GET_KEY_FN value; }; const SEC_GET_KEY_FN ACHAReqInfo::FixedValue<5>::value = nullptr; template <> template <> struct ACHAReqInfo::FixedValue<6> { static void* const value; }; void* const ACHAReqInfo::FixedValue<6>::value = nullptr; typedef AcquireCredentialsHandleAFB::Request ACHARequestHandler; typedef AcquireCredentialsHandleAFB::RequestDelegate ACHADelegateRequestHandler; template <> void ACHARequestHandler::Marshal(IpdlTuple& aTuple, const LPSTR& principal, const LPSTR& pkg, const unsigned long& credUse, const PVOID& logonId, const PVOID& auth, const SEC_GET_KEY_FN& getKeyFn, const PVOID& getKeyArg, const PCredHandle& cred, const PTimeStamp& expiry) { const PSCHANNEL_CRED& scCred = reinterpret_cast(auth); ACHADelegateRequestHandler::Marshal(aTuple, principal, pkg, credUse, logonId, scCred, getKeyFn, getKeyArg); } template <> bool ACHARequestHandler::Unmarshal(ServerCallData& aScd, const IpdlTuple& aTuple, LPSTR& principal, LPSTR& pkg, unsigned long& credUse, PVOID& logonId, PVOID& auth, SEC_GET_KEY_FN& getKeyFn, PVOID& getKeyArg, PCredHandle& cred, PTimeStamp& expiry) { PSCHANNEL_CRED& scCred = reinterpret_cast(auth); if (!ACHADelegateRequestHandler::Unmarshal(aScd, aTuple, principal, pkg, credUse, logonId, scCred, getKeyFn, getKeyArg)) { return false; } cred = aScd.Allocate(); expiry = aScd.Allocate<::TimeStamp>(); return true; } typedef ResponseInfo ACHARspInfo; // Response phase must send output parameters template <> template <> struct ACHARspInfo::ShouldMarshal<7> { static const bool value = true; }; template <> template <> struct ACHARspInfo::ShouldMarshal<8> { static const bool value = true; }; /* QueryCredentialsAttributesA */ typedef SslFunctionBroker QueryCredentialsAttributesAFB; template <> ShouldHookFunc* const QueryCredentialsAttributesAFB::BaseType::mShouldHook = &CheckQuirks; /* FreeCredentialsHandle */ typedef SslFunctionBroker FreeCredentialsHandleFB; template <> ShouldHookFunc* const FreeCredentialsHandleFB::BaseType::mShouldHook = &CheckQuirks; typedef FreeCredentialsHandleFB::Request FCHReq; template <> bool FCHReq::ShouldBroker(Endpoint endpoint, const PCredHandle& h) { // If we are server side then we were already validated since we had to be // looked up in the "uint64_t <-> CredHandle" hashtable. // In the client, we check that this is a dummy handle. return (endpoint == SERVER) || ((h->dwLower == h->dwUpper) && IsOdd(static_cast(h->dwLower))); } /* CreateMutexW */ // Get the user's SID as a string. Returns an empty string on failure. static std::wstring GetUserSid() { std::wstring ret; // Get user SID from process token information HANDLE token; BOOL success = ::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token); if (!success) { return ret; } DWORD bufLen; success = ::GetTokenInformation(token, TokenUser, nullptr, 0, &bufLen); if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { return ret; } void* buf = malloc(bufLen); success = ::GetTokenInformation(token, TokenUser, buf, bufLen, &bufLen); MOZ_ASSERT(success); if (success) { TOKEN_USER* tokenUser = static_cast(buf); PSID sid = tokenUser->User.Sid; LPWSTR sidStr; success = ::ConvertSidToStringSid(sid, &sidStr); if (success) { ret = sidStr; ::LocalFree(sidStr); } } free(buf); ::CloseHandle(token); return ret; } // Get the name Windows uses for the camera mutex. Returns an empty string // on failure. // The camera mutex is identified in Windows code using a hard-coded GUID // string, "eed3bd3a-a1ad-4e99-987b-d7cb3fcfa7f0", and the user's SID. The GUID // value was determined by investigating Windows code. It is referenced in // CCreateSwEnum::CCreateSwEnum(void) in devenum.dll. static std::wstring GetCameraMutexName() { std::wstring userSid = GetUserSid(); if (userSid.empty()) { return userSid; } return std::wstring(L"eed3bd3a-a1ad-4e99-987b-d7cb3fcfa7f0 - ") + userSid; } typedef FunctionBroker CreateMutexWFB; template <> ShouldHookFunc* const CreateMutexWFB::BaseType::mShouldHook = &CheckQuirks; typedef CreateMutexWFB::Request CMWReqHandler; typedef CMWReqHandler::Info CMWReqInfo; typedef CreateMutexWFB::Response CMWRspHandler; template <> bool CMWReqHandler::ShouldBroker(Endpoint endpoint, const LPSECURITY_ATTRIBUTES& aAttribs, const BOOL& aOwner, const LPCWSTR& aName) { // Statically hold the camera mutex name so that we dont recompute it for // every CreateMutexW call in the client process. static std::wstring camMutexName = GetCameraMutexName(); // Only broker if we are requesting the camera mutex. Note that we only // need to check that the client is actually requesting the camera. The // command is always valid on the server as long as we can construct the // mutex name. if (endpoint == SERVER) { return !camMutexName.empty(); } return (!aOwner) && aName && (!camMutexName.empty()) && (camMutexName == aName); } // We dont need to marshal any parameters. We construct all of them // server-side. template <> template <> struct CMWReqInfo::ShouldMarshal<0> { static const bool value = false; }; template <> template <> struct CMWReqInfo::ShouldMarshal<1> { static const bool value = false; }; template <> template <> struct CMWReqInfo::ShouldMarshal<2> { static const bool value = false; }; template <> template <> BROKER_DISABLE_CFGUARD HANDLE CreateMutexWFB::RunFunction( CreateMutexWFB::FunctionType* aOrigFunction, base::ProcessId aClientId, LPSECURITY_ATTRIBUTES& aAttribs, BOOL& aOwner, LPCWSTR& aName) const { // Use CreateMutexW to get the camera mutex and DuplicateHandle to open it // for use in the child process. // Recall that aAttribs, aOwner and aName are all unmarshaled so they are // unassigned garbage. SECURITY_ATTRIBUTES mutexAttrib = {sizeof(SECURITY_ATTRIBUTES), nullptr /* ignored */, TRUE}; std::wstring camMutexName = GetCameraMutexName(); if (camMutexName.empty()) { return 0; } HANDLE serverMutex = ::CreateMutexW(&mutexAttrib, FALSE, camMutexName.c_str()); if (serverMutex == 0) { return 0; } ScopedProcessHandle clientProcHandle; if (!base::OpenProcessHandle(aClientId, &clientProcHandle.rwget())) { return 0; } HANDLE ret; if (!::DuplicateHandle(::GetCurrentProcess(), serverMutex, clientProcHandle, &ret, SYNCHRONIZE, FALSE, DUPLICATE_CLOSE_SOURCE)) { return 0; } return ret; } #endif // defined(XP_WIN) /*****************************************************************************/ #define FUN_HOOK(x) static_cast(x) void AddBrokeredFunctionHooks(FunctionHookArray& aHooks) { // We transfer ownership of the FunctionHook objects to the array. #if defined(XP_WIN) aHooks[ID_GetKeyState] = FUN_HOOK(new GetKeyStateFB("user32.dll", "GetKeyState", &GetKeyState)); aHooks[ID_SetCursorPos] = FUN_HOOK(new SetCursorPosFB("user32.dll", "SetCursorPos", &SetCursorPos)); aHooks[ID_GetSaveFileNameW] = FUN_HOOK(new GetSaveFileNameWFB( "comdlg32.dll", "GetSaveFileNameW", &GetSaveFileNameW)); aHooks[ID_GetOpenFileNameW] = FUN_HOOK(new GetOpenFileNameWFB( "comdlg32.dll", "GetOpenFileNameW", &GetOpenFileNameW)); aHooks[ID_InternetOpenA] = FUN_HOOK( new InternetOpenAFB("wininet.dll", "InternetOpenA", &InternetOpenA)); aHooks[ID_InternetConnectA] = FUN_HOOK(new InternetConnectAFB( "wininet.dll", "InternetConnectA", &InternetConnectA)); aHooks[ID_InternetCloseHandle] = FUN_HOOK(new InternetCloseHandleFB( "wininet.dll", "InternetCloseHandle", &InternetCloseHandle)); aHooks[ID_InternetQueryDataAvailable] = FUN_HOOK(new InternetQueryDataAvailableFB("wininet.dll", "InternetQueryDataAvailable", &InternetQueryDataAvailable)); aHooks[ID_InternetReadFile] = FUN_HOOK(new InternetReadFileFB( "wininet.dll", "InternetReadFile", &InternetReadFile)); aHooks[ID_InternetWriteFile] = FUN_HOOK(new InternetWriteFileFB( "wininet.dll", "InternetWriteFile", &InternetWriteFile)); aHooks[ID_InternetSetOptionA] = FUN_HOOK(new InternetSetOptionAFB( "wininet.dll", "InternetSetOptionA", &InternetSetOptionA)); aHooks[ID_HttpAddRequestHeadersA] = FUN_HOOK(new HttpAddRequestHeadersAFB( "wininet.dll", "HttpAddRequestHeadersA", &HttpAddRequestHeadersA)); aHooks[ID_HttpOpenRequestA] = FUN_HOOK(new HttpOpenRequestAFB( "wininet.dll", "HttpOpenRequestA", &HttpOpenRequestA)); aHooks[ID_HttpQueryInfoA] = FUN_HOOK( new HttpQueryInfoAFB("wininet.dll", "HttpQueryInfoA", &HttpQueryInfoA)); aHooks[ID_HttpSendRequestA] = FUN_HOOK(new HttpSendRequestAFB( "wininet.dll", "HttpSendRequestA", &HttpSendRequestA)); aHooks[ID_HttpSendRequestExA] = FUN_HOOK(new HttpSendRequestExAFB( "wininet.dll", "HttpSendRequestExA", &HttpSendRequestExA)); aHooks[ID_HttpEndRequestA] = FUN_HOOK(new HttpEndRequestAFB( "wininet.dll", "HttpEndRequestA", &HttpEndRequestA)); aHooks[ID_InternetQueryOptionA] = FUN_HOOK(new InternetQueryOptionAFB( "wininet.dll", "InternetQueryOptionA", &InternetQueryOptionA)); aHooks[ID_InternetErrorDlg] = FUN_HOOK(new InternetErrorDlgFB( "wininet.dll", "InternetErrorDlg", InternetErrorDlg)); aHooks[ID_AcquireCredentialsHandleA] = FUN_HOOK(new AcquireCredentialsHandleAFB("sspicli.dll", "AcquireCredentialsHandleA", &AcquireCredentialsHandleA)); aHooks[ID_QueryCredentialsAttributesA] = FUN_HOOK(new QueryCredentialsAttributesAFB("sspicli.dll", "QueryCredentialsAttributesA", &QueryCredentialsAttributesA)); aHooks[ID_FreeCredentialsHandle] = FUN_HOOK(new FreeCredentialsHandleFB( "sspicli.dll", "FreeCredentialsHandle", &FreeCredentialsHandle)); aHooks[ID_CreateMutexW] = FUN_HOOK( new CreateMutexWFB("kernel32.dll", "CreateMutexW", &CreateMutexW)); #endif // defined(XP_WIN) } #undef FUN_HOOK } // namespace mozilla::plugins