/* -*- 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 { namespace 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<> 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<> 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) { nsTArray arrayAcceptTypes; LPCSTR * curAcceptType = acceptTypes; if (curAcceptType) { while (*curAcceptType) { arrayAcceptTypes.AppendElement(nsCString(*curAcceptType)); ++curAcceptType; } } 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) { nsTArray 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 = UNISP_NAME_A; 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<> 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 plugins } // namespace mozilla