tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SameBinary.h (4921B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef mozilla_SameBinary_h
      8 #define mozilla_SameBinary_h
      9 
     10 #include "mozilla/WinHeaderOnlyUtils.h"
     11 #include "mozilla/NativeNt.h"
     12 #include "nsWindowsHelpers.h"
     13 
     14 namespace mozilla {
     15 
     16 class ProcessImagePath final {
     17  PathType mType;
     18  LauncherVoidResult mLastError;
     19 
     20  // Using a larger buffer because an NT path may exceed MAX_PATH.
     21  WCHAR mPathBuffer[(MAX_PATH * 2) + 1];
     22 
     23 public:
     24  // Initialize with an NT path string of a given process handle
     25  explicit ProcessImagePath(const nsAutoHandle& aProcess)
     26      : mType(PathType::eNtPath), mLastError(Ok()) {
     27    DWORD len = std::size(mPathBuffer);
     28    if (!::QueryFullProcessImageNameW(aProcess.get(), PROCESS_NAME_NATIVE,
     29                                      mPathBuffer, &len)) {
     30      mLastError = LAUNCHER_ERROR_FROM_LAST();
     31      return;
     32    }
     33  }
     34 
     35  // Initizlize with a DOS path string of a given imagebase address
     36  explicit ProcessImagePath(HMODULE aImageBase)
     37      : mType(PathType::eDosPath), mLastError(Ok()) {
     38    DWORD len =
     39        ::GetModuleFileNameW(aImageBase, mPathBuffer, std::size(mPathBuffer));
     40    if (!len || len == std::size(mPathBuffer)) {
     41      mLastError = LAUNCHER_ERROR_FROM_LAST();
     42      return;
     43    }
     44  }
     45 
     46  bool IsError() const { return mLastError.isErr(); }
     47 
     48  const WindowsErrorType& GetError() const { return mLastError.inspectErr(); }
     49 
     50  FileUniqueId GetId() const { return FileUniqueId(mPathBuffer, mType); }
     51 
     52  bool CompareNtPaths(const ProcessImagePath& aOther) const {
     53    if (mLastError.isErr() || aOther.mLastError.isErr() ||
     54        mType != PathType::eNtPath || aOther.mType != PathType::eNtPath) {
     55      return false;
     56    }
     57 
     58    UNICODE_STRING path1, path2;
     59    ::RtlInitUnicodeString(&path1, mPathBuffer);
     60    ::RtlInitUnicodeString(&path2, aOther.mPathBuffer);
     61    return !!::RtlEqualUnicodeString(&path1, &path2, TRUE);
     62  }
     63 };
     64 
     65 enum class ImageFileCompareOption {
     66  Default,
     67  CompareNtPathsOnly,
     68 };
     69 
     70 static inline mozilla::LauncherResult<bool> IsSameBinaryAsParentProcess(
     71    ImageFileCompareOption aOption = ImageFileCompareOption::Default) {
     72  mozilla::LauncherResult<DWORD> parentPid = mozilla::nt::GetParentProcessId();
     73  if (parentPid.isErr()) {
     74    return parentPid.propagateErr();
     75  }
     76 
     77  nsAutoHandle parentProcess(::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,
     78                                           FALSE, parentPid.unwrap()));
     79  if (!parentProcess.get()) {
     80    DWORD err = ::GetLastError();
     81    if (err == ERROR_INVALID_PARAMETER || err == ERROR_ACCESS_DENIED) {
     82      // In the ERROR_INVALID_PARAMETER case, the process identified by
     83      // parentPid has already exited. This is a common case when the parent
     84      // process is not Firefox, thus we should return false instead of erroring
     85      // out.
     86      // The ERROR_ACCESS_DENIED case can happen when the parent process is
     87      // something that we don't have permission to query. For example, we may
     88      // encounter this when Firefox is launched by the Windows Task Scheduler.
     89      return false;
     90    }
     91 
     92    return LAUNCHER_ERROR_FROM_WIN32(err);
     93  }
     94 
     95  ProcessImagePath parentExe(parentProcess);
     96  if (parentExe.IsError()) {
     97    return ::mozilla::Err(parentExe.GetError());
     98  }
     99 
    100  if (aOption == ImageFileCompareOption::Default) {
    101    bool skipFileIdComparison = false;
    102 
    103    FileUniqueId id1 = parentExe.GetId();
    104    if (id1.IsError()) {
    105      // We saw a number of Win7 users failed to call NtOpenFile with
    106      // STATUS_OBJECT_PATH_NOT_FOUND for an unknown reason.  In this
    107      // particular case, we fall back to the logic to compare NT path
    108      // strings instead of a file id which will not fail because we don't
    109      // need to open a file handle.
    110 #if !defined(STATUS_OBJECT_PATH_NOT_FOUND)
    111      constexpr NTSTATUS STATUS_OBJECT_PATH_NOT_FOUND = 0xc000003a;
    112 #endif
    113      const LauncherError& err = id1.GetError();
    114      if (err.mError !=
    115          WindowsError::FromNtStatus(STATUS_OBJECT_PATH_NOT_FOUND)) {
    116        return ::mozilla::Err(err);
    117      }
    118 
    119      skipFileIdComparison = true;
    120    }
    121 
    122    if (!skipFileIdComparison) {
    123      ProcessImagePath ourExe(nullptr);
    124      if (ourExe.IsError()) {
    125        return ::mozilla::Err(ourExe.GetError());
    126      }
    127 
    128      FileUniqueId id2 = ourExe.GetId();
    129      if (id2.IsError()) {
    130        return ::mozilla::Err(id2.GetError());
    131      }
    132      return id1 == id2;
    133    }
    134  }
    135 
    136  nsAutoHandle ourProcess(::GetCurrentProcess());
    137  ProcessImagePath ourExeNt(ourProcess);
    138  if (ourExeNt.IsError()) {
    139    return ::mozilla::Err(ourExeNt.GetError());
    140  }
    141  return parentExe.CompareNtPaths(ourExeNt);
    142 }
    143 
    144 }  // namespace mozilla
    145 
    146 #endif  //  mozilla_SameBinary_h