blob: 01357867a0611b2a66289b3b1d045ddd89dc52fa [file] [log] [blame]
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/win/browser_util.h"
#include <windows.h>
// sddl.h must come after windows.h.
#include <winternl.h>
#include <sddl.h>
#include <algorithm>
#include <climits>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include "base/base_paths.h"
#include "base/check.h"
#include "base/files/file_path.h"
#include "base/path_service.h"
#include "base/win/scoped_handle.h"
#include "base/win/scoped_localalloc.h"
namespace browser_util {
namespace {
std::optional<std::wstring> GetNtPathFromWin32Path(const std::wstring& path) {
base::win::ScopedHandle file(::CreateFileW(
path.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr));
if (!file.is_valid()) {
return std::nullopt;
}
constexpr ULONG kMaxNameSize = USHRT_MAX + sizeof(UNICODE_STRING);
std::unique_ptr<BYTE[]> buffer = std::make_unique<BYTE[]>(kMaxNameSize);
DWORD return_length;
// Information class 1 is ObjectNameInformation.
NTSTATUS status =
::NtQueryObject(file.get(), static_cast<OBJECT_INFORMATION_CLASS>(1),
buffer.get(), kMaxNameSize, &return_length);
if (!NT_SUCCESS(status)) {
return std::nullopt;
}
PUNICODE_STRING name = reinterpret_cast<PUNICODE_STRING>(buffer.get());
return std::wstring(name->Buffer, name->Length / sizeof(name->Buffer[0]));
}
} // namespace
bool IsBrowserAlreadyRunning() {
static HANDLE handle = nullptr;
base::FilePath exe_dir_path;
// DIR_EXE is obtained from the path of FILE_EXE and, on Windows, FILE_EXE is
// obtained from reading the PEB of the currently running process. This means
// that even if the EXE file is moved, the DIR_EXE will still reflect the
// original location of the EXE from when it was started. This is important as
// IsBrowserAlreadyRunning must detect any running browser in Chrome's install
// directory, and not in a temporary directory if it is subsequently renamed
// or moved while running.
if (!base::PathService::Get(base::DIR_EXE, &exe_dir_path)) {
// If this fails, there isn't much that can be done. However, assuming that
// browser is *not* already running is the safer action here, as it means
// that any pending upgrade actions will occur and hopefully the issue that
// caused this failure will be resolved by the newer version. This might
// cause the currently running browser to be temporarily broken, but it's
// probably broken already if this API is failing.
return false;
}
std::optional<std::wstring> nt_dir_name =
GetNtPathFromWin32Path(exe_dir_path.value());
if (!nt_dir_name) {
// See above for why false is returned here.
return false;
}
std::replace(nt_dir_name->begin(), nt_dir_name->end(), '\\', '!');
std::ranges::transform(*nt_dir_name, nt_dir_name->begin(), tolower);
nt_dir_name = L"Global\\" + nt_dir_name.value();
if (handle != NULL)
::CloseHandle(std::exchange(handle, nullptr));
// For this to work for both user and system installs, we need the event to be
// accessible to all interactive users so that we can correctly detect any
// instance they are running. Otherwise, we can end up executing pending
// upgrade actions while there are instances running, resulting in reliability
// issues for one of the users.
// Security Descriptor for the global browser running event:
// SYSTEM : EVENT_ALL_ACCESS
// Interactive User : EVENT_ALL_ACCESS
static constexpr wchar_t kAllAccessDescriptor[] =
L"D:P(A;;0x1F0003;;;SY)(A;;0x1F0003;;;IU)";
SECURITY_ATTRIBUTES attributes = {sizeof(SECURITY_ATTRIBUTES), nullptr,
FALSE};
if (!::ConvertStringSecurityDescriptorToSecurityDescriptor(
kAllAccessDescriptor, SDDL_REVISION_1,
&attributes.lpSecurityDescriptor, nullptr)) {
// If this fails, it usually means the security descriptor string is
// incorrect. As a fallback, create the event with the default descriptor
// by setting it to nullptr. This works for single user devices which is the
// most common case for Windows.
DPCHECK(false);
attributes.lpSecurityDescriptor = nullptr;
}
base::win::ScopedLocalAlloc scoped_sd(attributes.lpSecurityDescriptor);
handle = ::CreateEventW(&attributes, /*bManualReset=*/TRUE,
/*bInitialState=*/TRUE, nt_dir_name->c_str());
int error = ::GetLastError();
// There is another browser running if `CreateEventW` succeeded and the object
// existed prior to the call or if `CreateEventW` failed due to access denied.
return error == (handle ? ERROR_ALREADY_EXISTS : ERROR_ACCESS_DENIED);
}
} // namespace browser_util