mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-06-05 21:39:12 +02:00
cefclient: win: Add code signing verification (see #3935)
Move code signing verification code to libcef_dll_wrapper and add example checks in cefclient. Load libcef.dll with code signing checks. Add a CefScopedLibraryLoader variant for Windows.
This commit is contained in:
58
BUILD.gn
58
BUILD.gn
@ -1169,6 +1169,9 @@ config("libcef_dll_wrapper_config") {
|
|||||||
# Increase the initial stack size to 8MiB from the default 1MiB.
|
# Increase the initial stack size to 8MiB from the default 1MiB.
|
||||||
ldflags = [ "/STACK:0x800000" ]
|
ldflags = [ "/STACK:0x800000" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Required to support CefScopedLibraryLoader.
|
||||||
|
ldflags += [ "/DELAYLOAD:libcef.dll" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
# Build using the minimum C++ version supported by the CEF binary distribution.
|
# Build using the minimum C++ version supported by the CEF binary distribution.
|
||||||
@ -1204,6 +1207,14 @@ static_library("libcef_dll_wrapper") {
|
|||||||
sources += gypi_paths2.libcef_dll_wrapper_sources_mac
|
sources += gypi_paths2.libcef_dll_wrapper_sources_mac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_win) {
|
||||||
|
sources += gypi_paths2.libcef_dll_wrapper_sources_win
|
||||||
|
libs = [
|
||||||
|
"crypt32.lib",
|
||||||
|
"wintrust.lib",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
defines = [ "WRAPPING_CEF_SHARED" ]
|
defines = [ "WRAPPING_CEF_SHARED" ]
|
||||||
|
|
||||||
configs += [ ":libcef_dll_wrapper_config" ]
|
configs += [ ":libcef_dll_wrapper_config" ]
|
||||||
@ -1247,13 +1258,15 @@ if (is_mac) {
|
|||||||
if (is_win) {
|
if (is_win) {
|
||||||
bootstrap_sources = includes_common +
|
bootstrap_sources = includes_common +
|
||||||
includes_win + [
|
includes_win + [
|
||||||
|
"include/wrapper/cef_certificate_util_win.h",
|
||||||
|
"include/wrapper/cef_util_win.h",
|
||||||
"libcef_dll/bootstrap/bootstrap_util_win.cc",
|
"libcef_dll/bootstrap/bootstrap_util_win.cc",
|
||||||
"libcef_dll/bootstrap/bootstrap_util_win.h",
|
"libcef_dll/bootstrap/bootstrap_util_win.h",
|
||||||
"libcef_dll/bootstrap/bootstrap_win.cc",
|
"libcef_dll/bootstrap/bootstrap_win.cc",
|
||||||
"libcef_dll/bootstrap/certificate_util_win.cc",
|
|
||||||
"libcef_dll/bootstrap/certificate_util_win.h",
|
|
||||||
"libcef_dll/bootstrap/win/bootstrap.rc",
|
"libcef_dll/bootstrap/win/bootstrap.rc",
|
||||||
"libcef_dll/bootstrap/win/resource.h",
|
"libcef_dll/bootstrap/win/resource.h",
|
||||||
|
"libcef_dll/wrapper/cef_certificate_util_win.cc",
|
||||||
|
"libcef_dll/wrapper/cef_util_win.cc",
|
||||||
"libcef/browser/preferred_stack_size_win.inc",
|
"libcef/browser/preferred_stack_size_win.inc",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1768,7 +1781,8 @@ if (is_mac) {
|
|||||||
]
|
]
|
||||||
|
|
||||||
# Delay-load as many DLLs as possible for sandbox and startup perf
|
# Delay-load as many DLLs as possible for sandbox and startup perf
|
||||||
# improvements.
|
# improvements. Don't use "delayloads_not_for_child_dll" here because
|
||||||
|
# we need some DLLs loaded in child processes before sandbox lockdown.
|
||||||
configs += [ "//build/config/win:delayloads" ]
|
configs += [ "//build/config/win:delayloads" ]
|
||||||
|
|
||||||
libs = [
|
libs = [
|
||||||
@ -2264,6 +2278,7 @@ if (is_mac) {
|
|||||||
|
|
||||||
if (is_win) {
|
if (is_win) {
|
||||||
sources += includes_win +
|
sources += includes_win +
|
||||||
|
gypi_paths2.includes_wrapper_win +
|
||||||
gypi_paths2.shared_sources_win +
|
gypi_paths2.shared_sources_win +
|
||||||
gypi_paths2.cefclient_sources_win +
|
gypi_paths2.cefclient_sources_win +
|
||||||
gypi_paths2.cefclient_sources_resources_win_rc
|
gypi_paths2.cefclient_sources_resources_win_rc
|
||||||
@ -2274,7 +2289,10 @@ if (is_mac) {
|
|||||||
|
|
||||||
# Delay-load as many DLLs as possible for sandbox and startup perf
|
# Delay-load as many DLLs as possible for sandbox and startup perf
|
||||||
# improvements.
|
# improvements.
|
||||||
configs += [ "//build/config/win:delayloads" ]
|
configs += [
|
||||||
|
"//build/config/win:delayloads",
|
||||||
|
"//build/config/win:delayloads_not_for_child_dll",
|
||||||
|
]
|
||||||
|
|
||||||
defines += [
|
defines += [
|
||||||
"CEF_USE_ATL",
|
"CEF_USE_ATL",
|
||||||
@ -2350,6 +2368,7 @@ if (is_mac) {
|
|||||||
sources = includes_common +
|
sources = includes_common +
|
||||||
includes_win +
|
includes_win +
|
||||||
gypi_paths2.includes_wrapper +
|
gypi_paths2.includes_wrapper +
|
||||||
|
gypi_paths2.includes_wrapper_win +
|
||||||
gypi_paths2.shared_sources_browser +
|
gypi_paths2.shared_sources_browser +
|
||||||
gypi_paths2.shared_sources_common +
|
gypi_paths2.shared_sources_common +
|
||||||
gypi_paths2.shared_sources_renderer +
|
gypi_paths2.shared_sources_renderer +
|
||||||
@ -2373,7 +2392,10 @@ if (is_mac) {
|
|||||||
|
|
||||||
# Delay-load as many DLLs as possible for sandbox and startup perf
|
# Delay-load as many DLLs as possible for sandbox and startup perf
|
||||||
# improvements.
|
# improvements.
|
||||||
configs += [ "//build/config/win:delayloads" ]
|
configs += [
|
||||||
|
"//build/config/win:delayloads",
|
||||||
|
"//build/config/win:delayloads_not_for_child_dll",
|
||||||
|
]
|
||||||
|
|
||||||
libs = [
|
libs = [
|
||||||
"comctl32.lib",
|
"comctl32.lib",
|
||||||
@ -2422,6 +2444,7 @@ if (is_mac) {
|
|||||||
|
|
||||||
if (is_win) {
|
if (is_win) {
|
||||||
sources += includes_win +
|
sources += includes_win +
|
||||||
|
gypi_paths2.includes_wrapper_win +
|
||||||
gypi_paths2.cefsimple_sources_win +
|
gypi_paths2.cefsimple_sources_win +
|
||||||
gypi_paths2.cefsimple_sources_resources_win_rc
|
gypi_paths2.cefsimple_sources_resources_win_rc
|
||||||
|
|
||||||
@ -2431,7 +2454,10 @@ if (is_mac) {
|
|||||||
|
|
||||||
# Delay-load as many DLLs as possible for sandbox and startup perf
|
# Delay-load as many DLLs as possible for sandbox and startup perf
|
||||||
# improvements.
|
# improvements.
|
||||||
configs += [ "//build/config/win:delayloads" ]
|
configs += [
|
||||||
|
"//build/config/win:delayloads",
|
||||||
|
"//build/config/win:delayloads_not_for_child_dll",
|
||||||
|
]
|
||||||
|
|
||||||
deps += [
|
deps += [
|
||||||
":cef_sandbox",
|
":cef_sandbox",
|
||||||
@ -2474,6 +2500,7 @@ if (is_mac) {
|
|||||||
sources = includes_common +
|
sources = includes_common +
|
||||||
includes_win +
|
includes_win +
|
||||||
gypi_paths2.includes_wrapper +
|
gypi_paths2.includes_wrapper +
|
||||||
|
gypi_paths2.includes_wrapper_win +
|
||||||
gypi_paths2.cefsimple_sources_common +
|
gypi_paths2.cefsimple_sources_common +
|
||||||
gypi_paths2.cefsimple_sources_win +
|
gypi_paths2.cefsimple_sources_win +
|
||||||
gypi_paths2.cefsimple_sources_resources_win_rc
|
gypi_paths2.cefsimple_sources_resources_win_rc
|
||||||
@ -2490,7 +2517,10 @@ if (is_mac) {
|
|||||||
|
|
||||||
# Delay-load as many DLLs as possible for sandbox and startup perf
|
# Delay-load as many DLLs as possible for sandbox and startup perf
|
||||||
# improvements.
|
# improvements.
|
||||||
configs += [ "//build/config/win:delayloads" ]
|
configs += [
|
||||||
|
"//build/config/win:delayloads",
|
||||||
|
"//build/config/win:delayloads_not_for_child_dll",
|
||||||
|
]
|
||||||
|
|
||||||
libs = [
|
libs = [
|
||||||
"comctl32.lib",
|
"comctl32.lib",
|
||||||
@ -2535,13 +2565,17 @@ if (is_mac) {
|
|||||||
]
|
]
|
||||||
|
|
||||||
if (is_win) {
|
if (is_win) {
|
||||||
sources += gypi_paths2.shared_sources_win +
|
sources += gypi_paths2.includes_wrapper_win +
|
||||||
|
gypi_paths2.shared_sources_win +
|
||||||
gypi_paths2.ceftests_sources_win +
|
gypi_paths2.ceftests_sources_win +
|
||||||
gypi_paths2.ceftests_sources_resources_win_rc
|
gypi_paths2.ceftests_sources_resources_win_rc
|
||||||
|
|
||||||
# Delay-load as many DLLs as possible for sandbox and startup perf
|
# Delay-load as many DLLs as possible for sandbox and startup perf
|
||||||
# improvements.
|
# improvements.
|
||||||
configs += [ "//build/config/win:delayloads" ]
|
configs += [
|
||||||
|
"//build/config/win:delayloads",
|
||||||
|
"//build/config/win:delayloads_not_for_child_dll",
|
||||||
|
]
|
||||||
|
|
||||||
deps += [
|
deps += [
|
||||||
":cef_sandbox",
|
":cef_sandbox",
|
||||||
@ -2584,6 +2618,7 @@ if (is_mac) {
|
|||||||
|
|
||||||
sources = includes_common +
|
sources = includes_common +
|
||||||
gypi_paths2.includes_wrapper +
|
gypi_paths2.includes_wrapper +
|
||||||
|
gypi_paths2.includes_wrapper_win +
|
||||||
gypi_paths2.shared_sources_browser +
|
gypi_paths2.shared_sources_browser +
|
||||||
gypi_paths2.shared_sources_common +
|
gypi_paths2.shared_sources_common +
|
||||||
gypi_paths2.shared_sources_renderer +
|
gypi_paths2.shared_sources_renderer +
|
||||||
@ -2607,7 +2642,10 @@ if (is_mac) {
|
|||||||
|
|
||||||
# Delay-load as many DLLs as possible for sandbox and startup perf
|
# Delay-load as many DLLs as possible for sandbox and startup perf
|
||||||
# improvements.
|
# improvements.
|
||||||
configs += [ "//build/config/win:delayloads" ]
|
configs += [
|
||||||
|
"//build/config/win:delayloads",
|
||||||
|
"//build/config/win:delayloads_not_for_child_dll",
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,9 +32,13 @@ DLLS_X64 = [
|
|||||||
# processes. Conversely, some DLLs must be loaded before sandbox lockdown. In
|
# processes. Conversely, some DLLs must be loaded before sandbox lockdown. In
|
||||||
# unsandboxed processes they will load when first needed. The linker will
|
# unsandboxed processes they will load when first needed. The linker will
|
||||||
# automatically ignore anything which is not linked to the binary at all (it is
|
# automatically ignore anything which is not linked to the binary at all (it is
|
||||||
# harmless to have an unmatched /delayload). This list should be kept in sync
|
# harmless to have an unmatched /delayload). Lists should be kept in sync with
|
||||||
# with Chromium's "delayloads" target from the //build/config/win/BUILD.gn file.
|
# targets from Chromium's //build/config/win/BUILD.gn file.
|
||||||
DELAYLOAD_DLLS = [
|
DELAYLOAD_DLLS = [
|
||||||
|
# Required to support CefScopedLibraryLoader.
|
||||||
|
"libcef.dll"
|
||||||
|
|
||||||
|
# "delayloads" target.
|
||||||
"api-ms-win-core-winrt-error-l1-1-0.dll",
|
"api-ms-win-core-winrt-error-l1-1-0.dll",
|
||||||
"api-ms-win-core-winrt-l1-1-0.dll",
|
"api-ms-win-core-winrt-l1-1-0.dll",
|
||||||
"api-ms-win-core-winrt-string-l1-1-0.dll",
|
"api-ms-win-core-winrt-string-l1-1-0.dll",
|
||||||
@ -76,16 +80,33 @@ DELAYLOAD_DLLS = [
|
|||||||
"winusb.dll",
|
"winusb.dll",
|
||||||
"wsock32.dll",
|
"wsock32.dll",
|
||||||
"wtsapi32.dll",
|
"wtsapi32.dll",
|
||||||
|
|
||||||
|
# "delayloads_not_for_child_dll" target.
|
||||||
|
"crypt32.dll",
|
||||||
|
"dbghelp.dll",
|
||||||
|
"dhcpcsvc.dll",
|
||||||
|
"dwrite.dll",
|
||||||
|
"iphlpapi.dll",
|
||||||
|
"oleaut32.dll",
|
||||||
|
"secur32.dll",
|
||||||
|
"userenv.dll",
|
||||||
|
"winhttp.dll",
|
||||||
|
"winmm.dll",
|
||||||
|
"winspool.drv",
|
||||||
|
"wintrust.dll",
|
||||||
|
"ws2_32.dll",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Standard link libraries.
|
# Standard link libraries.
|
||||||
STANDARD_LIBS = [
|
STANDARD_LIBS = [
|
||||||
"comctl32.lib",
|
"comctl32.lib",
|
||||||
|
"crypt32.lib",
|
||||||
"delayimp.lib",
|
"delayimp.lib",
|
||||||
"gdi32.lib",
|
"gdi32.lib",
|
||||||
"rpcrt4.lib",
|
"rpcrt4.lib",
|
||||||
"shlwapi.lib",
|
"shlwapi.lib",
|
||||||
"user32.lib",
|
"user32.lib",
|
||||||
|
"wintrust.lib",
|
||||||
"ws2_32.lib",
|
"ws2_32.lib",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -80,6 +80,11 @@
|
|||||||
'includes_wrapper_mac': [
|
'includes_wrapper_mac': [
|
||||||
'include/wrapper/cef_library_loader.h',
|
'include/wrapper/cef_library_loader.h',
|
||||||
],
|
],
|
||||||
|
'includes_wrapper_win': [
|
||||||
|
'include/wrapper/cef_certificate_util_win.h',
|
||||||
|
'include/wrapper/cef_library_loader.h',
|
||||||
|
'include/wrapper/cef_util_win.h',
|
||||||
|
],
|
||||||
'includes_win': [
|
'includes_win': [
|
||||||
'include/cef_sandbox_win.h',
|
'include/cef_sandbox_win.h',
|
||||||
'include/internal/cef_win.h',
|
'include/internal/cef_win.h',
|
||||||
@ -166,10 +171,15 @@
|
|||||||
'libcef_dll/wrapper/libcef_dll_wrapper2.cc',
|
'libcef_dll/wrapper/libcef_dll_wrapper2.cc',
|
||||||
],
|
],
|
||||||
'libcef_dll_wrapper_sources_mac': [
|
'libcef_dll_wrapper_sources_mac': [
|
||||||
'libcef_dll/wrapper/cef_library_loader_mac.mm',
|
'libcef_dll/wrapper/cef_scoped_library_loader_mac.mm',
|
||||||
'libcef_dll/wrapper/cef_scoped_sandbox_context_mac.mm',
|
'libcef_dll/wrapper/cef_scoped_sandbox_context_mac.mm',
|
||||||
'libcef_dll/wrapper/libcef_dll_dylib.cc',
|
'libcef_dll/wrapper/libcef_dll_dylib.cc',
|
||||||
],
|
],
|
||||||
|
'libcef_dll_wrapper_sources_win': [
|
||||||
|
'libcef_dll/wrapper/cef_certificate_util_win.cc',
|
||||||
|
'libcef_dll/wrapper/cef_scoped_library_loader_win.cc',
|
||||||
|
'libcef_dll/wrapper/cef_util_win.cc',
|
||||||
|
],
|
||||||
'shared_sources_browser': [
|
'shared_sources_browser': [
|
||||||
'tests/shared/browser/client_app_browser.cc',
|
'tests/shared/browser/client_app_browser.cc',
|
||||||
'tests/shared/browser/client_app_browser.h',
|
'tests/shared/browser/client_app_browser.h',
|
||||||
|
@ -432,15 +432,20 @@ if(OS_WINDOWS)
|
|||||||
list(APPEND CEF_LINKER_FLAGS_DEBUG
|
list(APPEND CEF_LINKER_FLAGS_DEBUG
|
||||||
/DEBUG # Generate debug information
|
/DEBUG # Generate debug information
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Delayload most libraries as the dlls are simply not required at startup (or
|
||||||
|
# at all, depending on the process type). Some dlls open handles when they are
|
||||||
|
# loaded, and we may not want them to be loaded in renderers or other sandboxed
|
||||||
|
# processes. Conversely, some dlls must be loaded before sandbox lockdown. In
|
||||||
|
# unsandboxed processes they will load when first needed. The linker will
|
||||||
|
# automatically ignore anything which is not linked to the binary at all (it is
|
||||||
|
# harmless to have an unmatched /delayload). Lists should be kept in sync with
|
||||||
|
# targets from Chromium's //build/config/win/BUILD.gn file.
|
||||||
set(CEF_DELAYLOAD_FLAGS
|
set(CEF_DELAYLOAD_FLAGS
|
||||||
# Delayload most libraries as the dlls are simply not required at startup (or
|
# Required to support CefScopedLibraryLoader.
|
||||||
# at all, depending on the process type). Some dlls open handles when they are
|
/DELAYLOAD:libcef.dll
|
||||||
# loaded, and we may not want them to be loaded in renderers or other sandboxed
|
|
||||||
# processes. Conversely, some dlls must be loaded before sandbox lockdown. In
|
# "delayloads" target.
|
||||||
# unsandboxed processes they will load when first needed. The linker will
|
|
||||||
# automatically ignore anything which is not linked to the binary at all (it is
|
|
||||||
# harmless to have an unmatched /delayload). This list should be kept in sync
|
|
||||||
# with Chromium's "delayloads" target from the //build/config/win/BUILD.gn file.
|
|
||||||
/DELAYLOAD:api-ms-win-core-winrt-error-l1-1-0.dll
|
/DELAYLOAD:api-ms-win-core-winrt-error-l1-1-0.dll
|
||||||
/DELAYLOAD:api-ms-win-core-winrt-l1-1-0.dll
|
/DELAYLOAD:api-ms-win-core-winrt-l1-1-0.dll
|
||||||
/DELAYLOAD:api-ms-win-core-winrt-string-l1-1-0.dll
|
/DELAYLOAD:api-ms-win-core-winrt-string-l1-1-0.dll
|
||||||
@ -482,6 +487,21 @@ if(OS_WINDOWS)
|
|||||||
/DELAYLOAD:winusb.dll
|
/DELAYLOAD:winusb.dll
|
||||||
/DELAYLOAD:wsock32.dll
|
/DELAYLOAD:wsock32.dll
|
||||||
/DELAYLOAD:wtsapi32.dll
|
/DELAYLOAD:wtsapi32.dll
|
||||||
|
|
||||||
|
# "delayloads_not_for_child_dll" target.
|
||||||
|
/DELAYLOAD:crypt32.dll
|
||||||
|
/DELAYLOAD:dbghelp.dll
|
||||||
|
/DELAYLOAD:dhcpcsvc.dll
|
||||||
|
/DELAYLOAD:dwrite.dll
|
||||||
|
/DELAYLOAD:iphlpapi.dll
|
||||||
|
/DELAYLOAD:oleaut32.dll
|
||||||
|
/DELAYLOAD:secur32.dll
|
||||||
|
/DELAYLOAD:userenv.dll
|
||||||
|
/DELAYLOAD:winhttp.dll
|
||||||
|
/DELAYLOAD:winmm.dll
|
||||||
|
/DELAYLOAD:winspool.drv
|
||||||
|
/DELAYLOAD:wintrust.dll
|
||||||
|
/DELAYLOAD:ws2_32.dll
|
||||||
)
|
)
|
||||||
list(APPEND CEF_EXE_LINKER_FLAGS
|
list(APPEND CEF_EXE_LINKER_FLAGS
|
||||||
# For executable targets.
|
# For executable targets.
|
||||||
@ -530,10 +550,12 @@ if(OS_WINDOWS)
|
|||||||
# Standard libraries.
|
# Standard libraries.
|
||||||
set(CEF_STANDARD_LIBS
|
set(CEF_STANDARD_LIBS
|
||||||
comctl32.lib
|
comctl32.lib
|
||||||
|
crypt32.lib
|
||||||
delayimp.lib
|
delayimp.lib
|
||||||
gdi32.lib
|
gdi32.lib
|
||||||
rpcrt4.lib
|
rpcrt4.lib
|
||||||
shlwapi.lib
|
shlwapi.lib
|
||||||
|
wintrust.lib
|
||||||
ws2_32.lib
|
ws2_32.lib
|
||||||
)
|
)
|
||||||
|
|
||||||
|
152
include/wrapper/cef_certificate_util_win.h
Normal file
152
include/wrapper/cef_certificate_util_win.h
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
// Copyright (c) 2025 Marshall A. Greenblatt. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the name Chromium Embedded
|
||||||
|
// Framework nor the names of its contributors may be used to endorse
|
||||||
|
// or promote products derived from this software without specific prior
|
||||||
|
// written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// The contents of this file are only available to applications that link
|
||||||
|
// against the libcef_dll_wrapper target.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef CEF_INCLUDE_WRAPPER_CEF_CERTIFICATE_UTIL_WIN_H_
|
||||||
|
#define CEF_INCLUDE_WRAPPER_CEF_CERTIFICATE_UTIL_WIN_H_
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace cef_certificate_util {
|
||||||
|
|
||||||
|
// SHA1 upper-case hex encoded = 40 characters.
|
||||||
|
inline constexpr size_t kThumbprintLength = 40U;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Structure populated by GetClientThumbprints().
|
||||||
|
///
|
||||||
|
struct ThumbprintsInfo {
|
||||||
|
public:
|
||||||
|
///
|
||||||
|
/// True if one or more signatures exist and all are valid.
|
||||||
|
///
|
||||||
|
bool IsSignedAndValid() const {
|
||||||
|
return !valid_thumbprints.empty() && errors.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// True if unsigned, or if one or more signatures exist and all are valid.
|
||||||
|
///
|
||||||
|
bool IsUnsignedOrValid() const {
|
||||||
|
return !has_signature || IsSignedAndValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// True if this and |other| have the same signature status. If
|
||||||
|
/// |allow_unsigned| is true then both may be unsigned. Otherwise, one or more
|
||||||
|
/// signatures must exist, all must be valid, and the primary fingerprint must
|
||||||
|
/// be the same for both.
|
||||||
|
///
|
||||||
|
bool IsSame(const ThumbprintsInfo& other, bool allow_unsigned) const {
|
||||||
|
if (allow_unsigned && !has_signature && !other.has_signature) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return IsSignedAndValid() &&
|
||||||
|
other.HasPrimaryThumbprint(valid_thumbprints[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// True if a valid primary signature exists and it matches the specified
|
||||||
|
/// |thumbprint|.
|
||||||
|
///
|
||||||
|
bool HasPrimaryThumbprint(const std::string& thumbprint) const {
|
||||||
|
return IsSignedAndValid() && valid_thumbprints[0] == thumbprint;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// True if a primary signature exists, irrespective of validity.
|
||||||
|
///
|
||||||
|
bool has_signature = false;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Thumbprints for signatures, if any, that passed verification.
|
||||||
|
///
|
||||||
|
std::vector<std::string> valid_thumbprints;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Thumbprints for signatures, if any, that failed verification. Will not be
|
||||||
|
/// populated if |verify_binary=true| was passed to GetClientThumbprints().
|
||||||
|
///
|
||||||
|
std::vector<std::string> invalid_thumbprints;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Errors (newline delimited) if any signatures failed verification.
|
||||||
|
///
|
||||||
|
std::wstring errors;
|
||||||
|
};
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Process client signatures for the binary at the specified abolute
|
||||||
|
/// |binary_path| and populate |info|. If |verify_binary| is true and the
|
||||||
|
/// primary signature fails verification then no further signatures will be
|
||||||
|
/// processed. For a code signing example and usage details see
|
||||||
|
/// https://github.com/chromiumembedded/cef/issues/3824#issuecomment-2892139995
|
||||||
|
///
|
||||||
|
void GetClientThumbprints(const std::wstring& binary_path,
|
||||||
|
bool verify_binary,
|
||||||
|
ThumbprintsInfo& info);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Evaluate the binary at the specified absolute |binary_path| for common
|
||||||
|
/// requirements and populate |info|. If the binary is code signed then all
|
||||||
|
/// signatures must be valid. If |thumbprint| is a SHA1 hash (e.g. 40 character
|
||||||
|
/// upper-case hex-encoded value) then the primary signature must match that
|
||||||
|
/// thumbprint. If |allow_unsigned| is true and |thumbprint| is nullptr then the
|
||||||
|
/// binary may be unsigned, otherwise it must be validly signed. Returns true if
|
||||||
|
/// all requirements are met.
|
||||||
|
///
|
||||||
|
bool ValidateCodeSigning(const std::wstring& binary_path,
|
||||||
|
const char* thumbprint,
|
||||||
|
bool allow_unsigned,
|
||||||
|
ThumbprintsInfo& info);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Same as ValidateCodeSigning, but failures result in a FATAL error and
|
||||||
|
/// application termination. Optionally populate |info| is validation succeeds.
|
||||||
|
/// Usage must be protected by cef::logging::ScopedEarlySupport if called prior
|
||||||
|
/// to libcef loading.
|
||||||
|
///
|
||||||
|
void ValidateCodeSigningAssert(const std::wstring& binary_path,
|
||||||
|
const char* thumbprint,
|
||||||
|
bool allow_unsigned,
|
||||||
|
ThumbprintsInfo* info = nullptr);
|
||||||
|
|
||||||
|
} // namespace cef_certificate_util
|
||||||
|
|
||||||
|
#endif // CEF_INCLUDE_WRAPPER_CEF_CERTIFICATE_UTIL_WIN_H_
|
@ -33,11 +33,11 @@
|
|||||||
|
|
||||||
#include "include/base/cef_build.h"
|
#include "include/base/cef_build.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#if defined(OS_MAC)
|
||||||
#include <string>
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif // __cplusplus
|
#endif
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Load the CEF library at the specified |path|. Returns true (1) on
|
/// Load the CEF library at the specified |path|. Returns true (1) on
|
||||||
@ -53,6 +53,12 @@ int cef_unload_library(void);
|
|||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // defined(OS_MAC)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#if defined(OS_MAC)
|
#if defined(OS_MAC)
|
||||||
|
|
||||||
@ -99,7 +105,7 @@ int cef_unload_library(void);
|
|||||||
/// }
|
/// }
|
||||||
/// </pre>
|
/// </pre>
|
||||||
///
|
///
|
||||||
class CefScopedLibraryLoader {
|
class CefScopedLibraryLoader final {
|
||||||
public:
|
public:
|
||||||
CefScopedLibraryLoader();
|
CefScopedLibraryLoader();
|
||||||
|
|
||||||
@ -125,10 +131,81 @@ class CefScopedLibraryLoader {
|
|||||||
private:
|
private:
|
||||||
bool Load(bool helper);
|
bool Load(bool helper);
|
||||||
|
|
||||||
bool loaded_;
|
bool loaded_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // defined(OS_MAC)
|
#elif defined(OS_WIN)
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Scoped helper for loading and unloading the CEF library at runtime from the
|
||||||
|
/// specific location on disk, with optional code signing verification. Must be
|
||||||
|
/// used in combination with the "/DELAYLOAD:libcef.dll" linker flag.
|
||||||
|
///
|
||||||
|
/// Example usage:
|
||||||
|
///
|
||||||
|
/// <pre>
|
||||||
|
/// #include "include/wrapper/cef_library_loader.h"
|
||||||
|
///
|
||||||
|
/// int APIENTRY wWinMain(HINSTANCE hInstance,
|
||||||
|
/// HINSTANCE hPrevInstance,
|
||||||
|
/// LPTSTR lpCmdLine,
|
||||||
|
/// int nCmdShow)
|
||||||
|
/// // Dynamically load the CEF library.
|
||||||
|
/// CefScopedLibraryLoader library_loader;
|
||||||
|
/// if (!library_loader.LoadInSubProcess() &&
|
||||||
|
/// !library_loader.LoadInMain(L"C:\Program Files\CEF\libcef.dll")) {
|
||||||
|
/// return 1;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // Continue with CEF initialization...
|
||||||
|
/// }
|
||||||
|
/// </pre>
|
||||||
|
///
|
||||||
|
class CefScopedLibraryLoader final {
|
||||||
|
public:
|
||||||
|
CefScopedLibraryLoader();
|
||||||
|
|
||||||
|
CefScopedLibraryLoader(const CefScopedLibraryLoader&) = delete;
|
||||||
|
CefScopedLibraryLoader& operator=(const CefScopedLibraryLoader&) = delete;
|
||||||
|
|
||||||
|
~CefScopedLibraryLoader();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Load the CEF library (libcef.dll) in the main process from the specified
|
||||||
|
/// absolute path. If libcef.dll is code signed then all signatures must be
|
||||||
|
/// valid. If |thumbprint| is a SHA1 hash (e.g. 40 character upper-case
|
||||||
|
/// hex-encoded value) then the primary signature must match that thumbprint.
|
||||||
|
/// If |allow_unsigned| is true and |thumbprint| is nullptr then libcef.dll
|
||||||
|
/// may be unsigned, otherwise it must be validly signed. Failure of code
|
||||||
|
/// signing requirements or DLL loading will result in a FATAL error and
|
||||||
|
/// application termination. Returns true if the load succeeds. Usage must be
|
||||||
|
/// protected by cef::logging::ScopedEarlySupport.
|
||||||
|
///
|
||||||
|
bool LoadInMainAssert(const wchar_t* dll_path,
|
||||||
|
const char* thumbprint,
|
||||||
|
bool allow_unsigned);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Load the CEF library (libcef.dll) in a sub-process that may be sandboxed.
|
||||||
|
/// The path will be determined based on command-line arguments for the
|
||||||
|
/// current process. Failure of DLL loading will result in a FATAL error and
|
||||||
|
/// application termination. Returns true if the load succeeds. Usage must be
|
||||||
|
/// protected by cef::logging::ScopedEarlySupport.
|
||||||
|
///
|
||||||
|
bool LoadInSubProcessAssert();
|
||||||
|
|
||||||
|
private:
|
||||||
|
HMODULE handle_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace switches {
|
||||||
|
// Changes to this value require rebuilding libcef.dll.
|
||||||
|
inline constexpr char kLibcefPath[] = "libcef-path";
|
||||||
|
inline constexpr wchar_t kLibcefPathW[] = L"libcef-path";
|
||||||
|
} // namespace switches
|
||||||
|
|
||||||
|
#endif // defined(OS_WIN)
|
||||||
#endif // __cplusplus
|
#endif // __cplusplus
|
||||||
|
|
||||||
#endif // CEF_INCLUDE_WRAPPER_CEF_LIBRARY_LOADER_H_
|
#endif // CEF_INCLUDE_WRAPPER_CEF_LIBRARY_LOADER_H_
|
||||||
|
65
include/wrapper/cef_util_win.h
Normal file
65
include/wrapper/cef_util_win.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// Copyright (c) 2025 Marshall A. Greenblatt. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the name Chromium Embedded
|
||||||
|
// Framework nor the names of its contributors may be used to endorse
|
||||||
|
// or promote products derived from this software without specific prior
|
||||||
|
// written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// The contents of this file are only available to applications that link
|
||||||
|
// against the libcef_dll_wrapper target.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef CEF_INCLUDE_WRAPPER_CEF_UTIL_WIN_H_
|
||||||
|
#define CEF_INCLUDE_WRAPPER_CEF_UTIL_WIN_H_
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace cef_util {
|
||||||
|
|
||||||
|
// Returns the fully qualified file path for the executable module.
|
||||||
|
std::wstring GetExePath();
|
||||||
|
|
||||||
|
// Returns the fully qualified file path for |module|.
|
||||||
|
std::wstring GetModulePath(HMODULE module);
|
||||||
|
|
||||||
|
// Returns the value of GetLastError() as a string.
|
||||||
|
std::wstring GetLastErrorAsString();
|
||||||
|
|
||||||
|
// Parse command line arguments for |hInstance|.
|
||||||
|
std::vector<std::wstring> ParseCommandLineArgs(const wchar_t* str);
|
||||||
|
|
||||||
|
// Returns the value for |name| in |command_line|, if any.
|
||||||
|
std::wstring GetCommandLineValue(const std::vector<std::wstring>& command_line,
|
||||||
|
const std::wstring& name);
|
||||||
|
|
||||||
|
} // namespace cef_util
|
||||||
|
|
||||||
|
#endif // CEF_INCLUDE_WRAPPER_CEF_UTIL_WIN_H_
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include "base/command_line.h"
|
#include "base/command_line.h"
|
||||||
#include "base/path_service.h"
|
#include "base/path_service.h"
|
||||||
|
#include "cef/include/wrapper/cef_library_loader.h"
|
||||||
#include "cef/libcef/browser/browser_frame.h"
|
#include "cef/libcef/browser/browser_frame.h"
|
||||||
#include "cef/libcef/browser/browser_host_base.h"
|
#include "cef/libcef/browser/browser_host_base.h"
|
||||||
#include "cef/libcef/browser/browser_info_manager.h"
|
#include "cef/libcef/browser/browser_info_manager.h"
|
||||||
@ -164,6 +165,18 @@ void HandleExternalProtocolHelper(
|
|||||||
isolation_info, nullptr);
|
isolation_info, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if BUILDFLAG(IS_WIN)
|
||||||
|
// Returns the module handle that contains this code (e.g. libcef.dll).
|
||||||
|
HINSTANCE GetCodeModuleHandle() {
|
||||||
|
HMODULE hModule = nullptr;
|
||||||
|
CHECK(::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
|
||||||
|
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
||||||
|
reinterpret_cast<LPCWSTR>(GetCodeModuleHandle),
|
||||||
|
&hModule));
|
||||||
|
return hModule;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
ChromeContentBrowserClientCef::ChromeContentBrowserClientCef() = default;
|
ChromeContentBrowserClientCef::ChromeContentBrowserClientCef() = default;
|
||||||
@ -220,12 +233,18 @@ void ChromeContentBrowserClientCef::AppendExtraCommandLineSwitches(
|
|||||||
|
|
||||||
#if BUILDFLAG(IS_WIN)
|
#if BUILDFLAG(IS_WIN)
|
||||||
{
|
{
|
||||||
const auto& module_value = bootstrap_util::GetValidatedModuleValue(
|
const auto& exe_path = bootstrap_util::GetExePath();
|
||||||
*browser_cmd, bootstrap_util::GetExePath());
|
const auto& module_value =
|
||||||
|
bootstrap_util::GetValidatedModuleValue(*browser_cmd, exe_path);
|
||||||
if (!module_value.empty()) {
|
if (!module_value.empty()) {
|
||||||
command_line->AppendSwitchNative(bootstrap_util::switches::kModule,
|
command_line->AppendSwitchNative(bootstrap_util::switches::kModule,
|
||||||
module_value);
|
module_value);
|
||||||
}
|
}
|
||||||
|
const auto& libcef_path =
|
||||||
|
bootstrap_util::GetModulePath(GetCodeModuleHandle());
|
||||||
|
if (libcef_path.DirName() != exe_path.DirName()) {
|
||||||
|
command_line->AppendSwitchPath(switches::kLibcefPath, libcef_path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ set(CEF_TARGET libcef_dll_wrapper)
|
|||||||
'autogen_capi_includes',
|
'autogen_capi_includes',
|
||||||
'includes_wrapper',
|
'includes_wrapper',
|
||||||
'includes_wrapper_mac:MAC',
|
'includes_wrapper_mac:MAC',
|
||||||
|
'includes_wrapper_win:WINDOWS',
|
||||||
'includes_win:WINDOWS',
|
'includes_win:WINDOWS',
|
||||||
'includes_win_capi:WINDOWS',
|
'includes_win_capi:WINDOWS',
|
||||||
'includes_mac:MAC',
|
'includes_mac:MAC',
|
||||||
@ -38,6 +39,7 @@ set(CEF_TARGET libcef_dll_wrapper)
|
|||||||
'libcef_dll_wrapper_sources_base',
|
'libcef_dll_wrapper_sources_base',
|
||||||
'libcef_dll_wrapper_sources_common',
|
'libcef_dll_wrapper_sources_common',
|
||||||
'libcef_dll_wrapper_sources_mac:MAC',
|
'libcef_dll_wrapper_sources_mac:MAC',
|
||||||
|
'libcef_dll_wrapper_sources_win:WINDOWS',
|
||||||
'autogen_client_side',
|
'autogen_client_side',
|
||||||
],
|
],
|
||||||
}}
|
}}
|
||||||
|
@ -11,6 +11,7 @@ namespace bootstrap_util {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
// Changes to these values require rebuilding libcef.dll.
|
||||||
constexpr wchar_t kWindowsSelfName[] = TEXT("bootstrap");
|
constexpr wchar_t kWindowsSelfName[] = TEXT("bootstrap");
|
||||||
constexpr wchar_t kConsoleSelfName[] = TEXT("bootstrapc");
|
constexpr wchar_t kConsoleSelfName[] = TEXT("bootstrapc");
|
||||||
|
|
||||||
@ -78,28 +79,4 @@ bool IsModulePathAllowed(const base::FilePath& module_path,
|
|||||||
return module_path.DirName() == exe_path.DirName();
|
return module_path.DirName() == exe_path.DirName();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::wstring GetLastErrorAsString() {
|
|
||||||
std::wstring error_message;
|
|
||||||
|
|
||||||
DWORD error_message_id = ::GetLastError();
|
|
||||||
if (error_message_id == 0) {
|
|
||||||
return error_message;
|
|
||||||
}
|
|
||||||
|
|
||||||
LPWSTR message_buffer = NULL;
|
|
||||||
|
|
||||||
DWORD size = FormatMessage(
|
|
||||||
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
|
||||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
||||||
NULL, error_message_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
||||||
(LPWSTR)&message_buffer, 0, NULL);
|
|
||||||
|
|
||||||
if (message_buffer) {
|
|
||||||
error_message = std::wstring(message_buffer, size);
|
|
||||||
LocalFree(message_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return error_message;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace bootstrap_util
|
} // namespace bootstrap_util
|
||||||
|
@ -19,8 +19,9 @@ class CommandLine;
|
|||||||
namespace bootstrap_util {
|
namespace bootstrap_util {
|
||||||
|
|
||||||
namespace switches {
|
namespace switches {
|
||||||
|
// Changes to this value require rebuilding libcef.dll.
|
||||||
inline constexpr char kModule[] = "module";
|
inline constexpr char kModule[] = "module";
|
||||||
}
|
} // namespace switches
|
||||||
|
|
||||||
// Returns true if |name| is one of the default bootstrap executable names.
|
// Returns true if |name| is one of the default bootstrap executable names.
|
||||||
bool IsDefaultExeName(const std::wstring& name);
|
bool IsDefaultExeName(const std::wstring& name);
|
||||||
@ -47,8 +48,6 @@ std::wstring GetDefaultModuleValue(const base::FilePath& exe_path);
|
|||||||
bool IsModulePathAllowed(const base::FilePath& module_path,
|
bool IsModulePathAllowed(const base::FilePath& module_path,
|
||||||
const base::FilePath& exe_path);
|
const base::FilePath& exe_path);
|
||||||
|
|
||||||
std::wstring GetLastErrorAsString();
|
|
||||||
|
|
||||||
} // namespace bootstrap_util
|
} // namespace bootstrap_util
|
||||||
|
|
||||||
#endif // CEF_LIBCEF_DLL_BOOTSTRAP_BOOTSTRAP_UTIL_WIN_H_
|
#endif // CEF_LIBCEF_DLL_BOOTSTRAP_BOOTSTRAP_UTIL_WIN_H_
|
||||||
|
@ -14,9 +14,10 @@
|
|||||||
#include "base/strings/utf_string_conversions.h"
|
#include "base/strings/utf_string_conversions.h"
|
||||||
#include "cef/include/cef_sandbox_win.h"
|
#include "cef/include/cef_sandbox_win.h"
|
||||||
#include "cef/include/internal/cef_types.h"
|
#include "cef/include/internal/cef_types.h"
|
||||||
|
#include "cef/include/wrapper/cef_certificate_util_win.h"
|
||||||
|
#include "cef/include/wrapper/cef_util_win.h"
|
||||||
#include "cef/libcef/browser/preferred_stack_size_win.inc"
|
#include "cef/libcef/browser/preferred_stack_size_win.inc"
|
||||||
#include "cef/libcef_dll/bootstrap/bootstrap_util_win.h"
|
#include "cef/libcef_dll/bootstrap/bootstrap_util_win.h"
|
||||||
#include "cef/libcef_dll/bootstrap/certificate_util_win.h"
|
|
||||||
#include "cef/libcef_dll/bootstrap/win/resource.h"
|
#include "cef/libcef_dll/bootstrap/win/resource.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -105,7 +106,7 @@ int APIENTRY wWinMain(HINSTANCE hInstance,
|
|||||||
|
|
||||||
std::wstring dll_name;
|
std::wstring dll_name;
|
||||||
base::FilePath exe_path;
|
base::FilePath exe_path;
|
||||||
certificate_util::ThumbprintsInfo exe_thumbprints;
|
cef_certificate_util::ThumbprintsInfo exe_thumbprints;
|
||||||
|
|
||||||
if (is_sandboxed) {
|
if (is_sandboxed) {
|
||||||
// Running as a sandboxed sub-process. May already be locked down, so we
|
// Running as a sandboxed sub-process. May already be locked down, so we
|
||||||
@ -135,7 +136,7 @@ int APIENTRY wWinMain(HINSTANCE hInstance,
|
|||||||
return CEF_RESULT_CODE_KILLED;
|
return CEF_RESULT_CODE_KILLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
certificate_util::GetClientThumbprints(
|
cef_certificate_util::GetClientThumbprints(
|
||||||
exe_path.value(), /*verify_binary=*/true, exe_thumbprints);
|
exe_path.value(), /*verify_binary=*/true, exe_thumbprints);
|
||||||
|
|
||||||
// The executable must either be unsigned or have all valid signatures.
|
// The executable must either be unsigned or have all valid signatures.
|
||||||
@ -175,8 +176,8 @@ int APIENTRY wWinMain(HINSTANCE hInstance,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (error.empty()) {
|
if (error.empty()) {
|
||||||
certificate_util::ThumbprintsInfo dll_thumbprints;
|
cef_certificate_util::ThumbprintsInfo dll_thumbprints;
|
||||||
certificate_util::GetClientThumbprints(
|
cef_certificate_util::GetClientThumbprints(
|
||||||
dll_path.value(), /*verify_binary=*/true, dll_thumbprints);
|
dll_path.value(), /*verify_binary=*/true, dll_thumbprints);
|
||||||
|
|
||||||
// The DLL and EXE must either both be unsigned or both have all valid
|
// The DLL and EXE must either both be unsigned or both have all valid
|
||||||
@ -201,7 +202,7 @@ int APIENTRY wWinMain(HINSTANCE hInstance,
|
|||||||
} else if (!is_sandboxed) {
|
} else if (!is_sandboxed) {
|
||||||
const auto subst = std::to_array<std::u16string>(
|
const auto subst = std::to_array<std::u16string>(
|
||||||
{base::WideToUTF16(dll_name),
|
{base::WideToUTF16(dll_name),
|
||||||
base::WideToUTF16(bootstrap_util::GetLastErrorAsString()),
|
base::WideToUTF16(cef_util::GetLastErrorAsString()),
|
||||||
base::ASCIIToUTF16(std::string(kProcName))});
|
base::ASCIIToUTF16(std::string(kProcName))});
|
||||||
error = FormatErrorString(IDS_ERROR_NO_PROC_EXPORT, subst);
|
error = FormatErrorString(IDS_ERROR_NO_PROC_EXPORT, subst);
|
||||||
}
|
}
|
||||||
@ -211,7 +212,7 @@ int APIENTRY wWinMain(HINSTANCE hInstance,
|
|||||||
} else if (!is_sandboxed) {
|
} else if (!is_sandboxed) {
|
||||||
const auto subst = std::to_array<std::u16string>(
|
const auto subst = std::to_array<std::u16string>(
|
||||||
{base::WideToUTF16(dll_name),
|
{base::WideToUTF16(dll_name),
|
||||||
base::WideToUTF16(bootstrap_util::GetLastErrorAsString())});
|
base::WideToUTF16(cef_util::GetLastErrorAsString())});
|
||||||
error = FormatErrorString(IDS_ERROR_LOAD_FAILED, subst);
|
error = FormatErrorString(IDS_ERROR_LOAD_FAILED, subst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
// Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
|
|
||||||
// reserved. Use of this source code is governed by a BSD-style license that
|
|
||||||
// can be found in the LICENSE file.
|
|
||||||
|
|
||||||
#ifndef CEF_LIBCEF_DLL_BOOTSTRAP_CERTIFICATE_UTIL_WIN_H_
|
|
||||||
#define CEF_LIBCEF_DLL_BOOTSTRAP_CERTIFICATE_UTIL_WIN_H_
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace certificate_util {
|
|
||||||
|
|
||||||
struct ThumbprintsInfo {
|
|
||||||
public:
|
|
||||||
bool IsSignedAndValid() const {
|
|
||||||
return !valid_thumbprints.empty() && errors.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsUnsignedOrValid() const {
|
|
||||||
return !has_signature || IsSignedAndValid();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsSame(const ThumbprintsInfo& other, bool allow_unsigned) const {
|
|
||||||
if (allow_unsigned && !has_signature && !other.has_signature) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if both are valid and have the same primary thumbprint.
|
|
||||||
return IsSignedAndValid() && other.IsSignedAndValid() &&
|
|
||||||
valid_thumbprints[0] == other.valid_thumbprints[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// True if a primary signature exists, irrespective of validity.
|
|
||||||
bool has_signature = false;
|
|
||||||
|
|
||||||
// Thumbprints for signatures, if any, that passed verification.
|
|
||||||
std::vector<std::string> valid_thumbprints;
|
|
||||||
|
|
||||||
// Thumbprints for signatures, if any, that failed verification. Will not be
|
|
||||||
// populated if |verify_binary=true| was passed to GetClientThumbprints().
|
|
||||||
std::vector<std::string> invalid_thumbprints;
|
|
||||||
|
|
||||||
// Errors (newline delimited) if any signatures failed verification.
|
|
||||||
std::wstring errors;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Process client signatures for the binary at |binary_path| and populate
|
|
||||||
// |info|. If |verify_binary| is true and the primary signature fails
|
|
||||||
// verification then no further signatures will be processed.
|
|
||||||
void GetClientThumbprints(const std::wstring& binary_path,
|
|
||||||
bool verify_binary,
|
|
||||||
ThumbprintsInfo& info);
|
|
||||||
|
|
||||||
} // namespace certificate_util
|
|
||||||
|
|
||||||
#endif // CEF_LIBCEF_DLL_BOOTSTRAP_CERTIFICATE_UTIL_WIN_H_
|
|
@ -2,19 +2,26 @@
|
|||||||
// reserved. Use of this source code is governed by a BSD-style license that
|
// reserved. Use of this source code is governed by a BSD-style license that
|
||||||
// can be found in the LICENSE file.
|
// can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "cef/libcef_dll/bootstrap/certificate_util_win.h"
|
#include "include/wrapper/cef_certificate_util_win.h"
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#include <Softpub.h>
|
#include <Softpub.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <wincrypt.h>
|
#include <wincrypt.h>
|
||||||
#include <wintrust.h>
|
#include <wintrust.h>
|
||||||
|
|
||||||
#include "base/strings/stringprintf.h"
|
#include <algorithm>
|
||||||
#include "base/strings/utf_string_conversions.h"
|
|
||||||
#include "cef/libcef_dll/bootstrap/bootstrap_util_win.h"
|
|
||||||
|
|
||||||
namespace certificate_util {
|
#include "include/wrapper/cef_util_win.h"
|
||||||
|
|
||||||
|
#if defined(CEF_BUILD_BOOTSTRAP)
|
||||||
|
#include "base/logging.h"
|
||||||
|
#else
|
||||||
|
#include "include/base/cef_logging.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace cef_certificate_util {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@ -37,23 +44,43 @@ bool InitCryptProviderStructs(WINTRUST_DATA& win_trust_data,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string BytesToHexString(const void* bytes, size_t length) {
|
// Returns bytes as an upper-case hex string.
|
||||||
const unsigned char* bytes_c = reinterpret_cast<const unsigned char*>(bytes);
|
std::string BytesToHexString(const BYTE* bytes, size_t length) {
|
||||||
|
|
||||||
std::string hex_string;
|
std::string hex_string;
|
||||||
hex_string.reserve(length * 2);
|
hex_string.resize(length * 2);
|
||||||
for (size_t index = 0; index < length; ++index) {
|
for (size_t index = 0; index < length; ++index) {
|
||||||
hex_string.append(base::StringPrintf("%02x", bytes_c[index]));
|
sprintf_s(&hex_string[2 * index], 3, "%02X", bytes[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return hex_string;
|
return hex_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::wstring ErrorPrefix(DWORD i) {
|
||||||
|
return L"\nCertificate " + std::to_wstring(i) + L": ";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring NormalizeError(const std::wstring& err) {
|
||||||
|
std::wstring str = err;
|
||||||
|
// Replace newlines.
|
||||||
|
std::replace(str.begin(), str.end(), L'\n', L' ');
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring GetBinaryName(const std::wstring& path) {
|
||||||
|
auto sep_pos = path.find_last_of(L"/\\");
|
||||||
|
if (sep_pos == std::wstring::npos) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
return path.substr(0, sep_pos);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void GetClientThumbprints(const std::wstring& binary_path,
|
void GetClientThumbprints(const std::wstring& binary_path,
|
||||||
bool verify_binary,
|
bool verify_binary,
|
||||||
ThumbprintsInfo& info) {
|
ThumbprintsInfo& info) {
|
||||||
|
info = {};
|
||||||
|
|
||||||
const HWND wvt_handle = static_cast<HWND>(INVALID_HANDLE_VALUE);
|
const HWND wvt_handle = static_cast<HWND>(INVALID_HANDLE_VALUE);
|
||||||
GUID wvt_policy = WINTRUST_ACTION_GENERIC_VERIFY_V2;
|
GUID wvt_policy = WINTRUST_ACTION_GENERIC_VERIFY_V2;
|
||||||
|
|
||||||
@ -71,9 +98,6 @@ void GetClientThumbprints(const std::wstring& binary_path,
|
|||||||
// After the first WinVerifyTrust call succeeds, we will continue inspecting
|
// After the first WinVerifyTrust call succeeds, we will continue inspecting
|
||||||
// the rest of the signatures.
|
// the rest of the signatures.
|
||||||
for (DWORD i = 0; i < sig_settings.cSecondarySigs + 1; ++i) {
|
for (DWORD i = 0; i < sig_settings.cSecondarySigs + 1; ++i) {
|
||||||
const auto& error_prefix =
|
|
||||||
base::ASCIIToWide(base::StringPrintf("\nCertificate %d: ", i));
|
|
||||||
|
|
||||||
WINTRUST_DATA win_trust_data = {0};
|
WINTRUST_DATA win_trust_data = {0};
|
||||||
win_trust_data.cbStruct = sizeof(win_trust_data);
|
win_trust_data.cbStruct = sizeof(win_trust_data);
|
||||||
win_trust_data.dwUIChoice = WTD_UI_NONE;
|
win_trust_data.dwUIChoice = WTD_UI_NONE;
|
||||||
@ -102,8 +126,8 @@ void GetClientThumbprints(const std::wstring& binary_path,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info.errors += error_prefix + TEXT("WinVerifyTrust failed: ") +
|
info.errors += ErrorPrefix(i) + L"WinVerifyTrust failed: " +
|
||||||
bootstrap_util::GetLastErrorAsString();
|
cef_util::GetLastErrorAsString();
|
||||||
|
|
||||||
// WinVerifyTrust will fail if the signing certificates can't be verified,
|
// WinVerifyTrust will fail if the signing certificates can't be verified,
|
||||||
// but it will still provide information about them in the StateData
|
// but it will still provide information about them in the StateData
|
||||||
@ -120,7 +144,7 @@ void GetClientThumbprints(const std::wstring& binary_path,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!win_trust_data.hWVTStateData) {
|
if (!win_trust_data.hWVTStateData) {
|
||||||
info.errors += error_prefix + TEXT("No WinVerifyTrust data");
|
info.errors += ErrorPrefix(i) + L"No WinVerifyTrust data";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,12 +173,12 @@ void GetClientThumbprints(const std::wstring& binary_path,
|
|||||||
thumbprints.emplace_back(
|
thumbprints.emplace_back(
|
||||||
BytesToHexString(sha1_bytes, sha1_bytes_count));
|
BytesToHexString(sha1_bytes, sha1_bytes_count));
|
||||||
} else {
|
} else {
|
||||||
info.errors += error_prefix +
|
info.errors += ErrorPrefix(i) +
|
||||||
TEXT("CertGetCertificateContextProperty failed: ") +
|
L"CertGetCertificateContextProperty failed: " +
|
||||||
bootstrap_util::GetLastErrorAsString();
|
cef_util::GetLastErrorAsString();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
info.errors += error_prefix + TEXT("Invalid WinVerifyTrust data");
|
info.errors += ErrorPrefix(i) + L"Invalid WinVerifyTrust data";
|
||||||
}
|
}
|
||||||
|
|
||||||
win_trust_data.dwStateAction = WTD_STATEACTION_CLOSE;
|
win_trust_data.dwStateAction = WTD_STATEACTION_CLOSE;
|
||||||
@ -164,4 +188,56 @@ void GetClientThumbprints(const std::wstring& binary_path,
|
|||||||
info.has_signature = true;
|
info.has_signature = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace certificate_util
|
bool ValidateCodeSigning(const std::wstring& binary_path,
|
||||||
|
const char* thumbprint,
|
||||||
|
bool allow_unsigned,
|
||||||
|
ThumbprintsInfo& info) {
|
||||||
|
GetClientThumbprints(binary_path.data(), /*verify_binary=*/true, info);
|
||||||
|
if (!info.errors.empty()) {
|
||||||
|
// The binary is signed, but one or more of the signatures failed
|
||||||
|
// validation.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool thumbprint_required =
|
||||||
|
thumbprint && std::strlen(thumbprint) == kThumbprintLength;
|
||||||
|
allow_unsigned &= !thumbprint_required;
|
||||||
|
|
||||||
|
if (thumbprint_required) {
|
||||||
|
if (!info.HasPrimaryThumbprint(thumbprint)) {
|
||||||
|
// The DLL is unsigned or the primary signature does not match the
|
||||||
|
// required thumbprint.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!allow_unsigned && !info.has_signature) {
|
||||||
|
// The DLL in unsigned which is not allowed.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValidateCodeSigningAssert(const std::wstring& binary_path,
|
||||||
|
const char* thumbprint,
|
||||||
|
bool allow_unsigned,
|
||||||
|
ThumbprintsInfo* info) {
|
||||||
|
cef_certificate_util::ThumbprintsInfo thumbprints;
|
||||||
|
bool valid = cef_certificate_util::ValidateCodeSigning(
|
||||||
|
binary_path.data(), thumbprint, allow_unsigned, thumbprints);
|
||||||
|
if (!thumbprints.errors.empty()) {
|
||||||
|
// The DLL is signed, but one or more of the signatures failed validation.
|
||||||
|
LOG(FATAL) << "Failed " << GetBinaryName(binary_path)
|
||||||
|
<< " certificate validation: "
|
||||||
|
<< NormalizeError(thumbprints.errors);
|
||||||
|
}
|
||||||
|
if (!valid) {
|
||||||
|
// Failed other requirements.
|
||||||
|
LOG(FATAL) << "Failed " << GetBinaryName(binary_path)
|
||||||
|
<< " validation requirements.";
|
||||||
|
}
|
||||||
|
if (info) {
|
||||||
|
*info = thumbprints;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace cef_certificate_util
|
@ -2,8 +2,6 @@
|
|||||||
// reserved. Use of this source code is governed by a BSD-style license that
|
// reserved. Use of this source code is governed by a BSD-style license that
|
||||||
// can be found in the LICENSE file.
|
// can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "include/wrapper/cef_library_loader.h"
|
|
||||||
|
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
#include <mach-o/dyld.h>
|
#include <mach-o/dyld.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -11,6 +9,8 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "include/wrapper/cef_library_loader.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
const char kFrameworkPath[] =
|
const char kFrameworkPath[] =
|
||||||
@ -46,7 +46,7 @@ std::string GetFrameworkPath(bool helper) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
CefScopedLibraryLoader::CefScopedLibraryLoader() : loaded_(false) {}
|
CefScopedLibraryLoader::CefScopedLibraryLoader() = default;
|
||||||
|
|
||||||
bool CefScopedLibraryLoader::Load(bool helper) {
|
bool CefScopedLibraryLoader::Load(bool helper) {
|
||||||
if (loaded_) {
|
if (loaded_) {
|
101
libcef_dll/wrapper/cef_scoped_library_loader_win.cc
Normal file
101
libcef_dll/wrapper/cef_scoped_library_loader_win.cc
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
|
||||||
|
// reserved. Use of this source code is governed by a BSD-style license that
|
||||||
|
// can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "include/base/cef_logging.h"
|
||||||
|
#include "include/wrapper/cef_certificate_util_win.h"
|
||||||
|
#include "include/wrapper/cef_library_loader.h"
|
||||||
|
#include "include/wrapper/cef_util_win.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
bool IsEqualIgnoringCase(const std::wstring& str1, const std::wstring& str2) {
|
||||||
|
return std::equal(
|
||||||
|
str1.begin(), str1.end(), str2.begin(), str2.end(),
|
||||||
|
[](wchar_t a, wchar_t b) { return std::tolower(a) == std::tolower(b); });
|
||||||
|
}
|
||||||
|
|
||||||
|
HMODULE Load(const std::wstring& dll_path,
|
||||||
|
const char* thumbprint,
|
||||||
|
bool allow_unsigned,
|
||||||
|
bool is_subprocess) {
|
||||||
|
if (!is_subprocess) {
|
||||||
|
// Load the client DLL as untrusted (e.g. without executing DllMain or
|
||||||
|
// loading additional modules) so that we can first check requirements.
|
||||||
|
// LoadLibrary's "default search order" is tricky and we don't want to guess
|
||||||
|
// about what DLL it will load. DONT_RESOLVE_DLL_REFERENCES is the only
|
||||||
|
// option that doesn't execute DllMain while still allowing us retrieve the
|
||||||
|
// path using GetModuleFileName. No execution of the DLL should be attempted
|
||||||
|
// while loaded in this mode.
|
||||||
|
if (HMODULE hModule = ::LoadLibraryEx(
|
||||||
|
dll_path.data(), nullptr,
|
||||||
|
DONT_RESOLVE_DLL_REFERENCES | LOAD_WITH_ALTERED_SEARCH_PATH)) {
|
||||||
|
const auto& module_path = cef_util::GetModulePath(hModule);
|
||||||
|
if (!IsEqualIgnoringCase(module_path, dll_path)) {
|
||||||
|
LOG(FATAL) << "Found libcef.dll at unexpected path";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a FATAL error and crash if validation fails.
|
||||||
|
cef_certificate_util::ValidateCodeSigningAssert(dll_path, thumbprint,
|
||||||
|
allow_unsigned);
|
||||||
|
|
||||||
|
FreeLibrary(hModule);
|
||||||
|
} else {
|
||||||
|
LOG(FATAL) << "Failed to load libcef.dll";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load normally.
|
||||||
|
if (HMODULE hModule = ::LoadLibraryEx(dll_path.data(), nullptr,
|
||||||
|
LOAD_WITH_ALTERED_SEARCH_PATH)) {
|
||||||
|
return hModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(FATAL) << "Failed to load libcef.dll";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
CefScopedLibraryLoader::CefScopedLibraryLoader() = default;
|
||||||
|
|
||||||
|
bool CefScopedLibraryLoader::LoadInMainAssert(const wchar_t* dll_path,
|
||||||
|
const char* thumbprint,
|
||||||
|
bool allow_unsigned) {
|
||||||
|
CHECK(!handle_);
|
||||||
|
handle_ = Load(dll_path, thumbprint, allow_unsigned, false);
|
||||||
|
return handle_ != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CefScopedLibraryLoader::LoadInSubProcessAssert() {
|
||||||
|
CHECK(!handle_);
|
||||||
|
|
||||||
|
constexpr wchar_t kProcessTypeW[] = L"type";
|
||||||
|
const auto& command_line =
|
||||||
|
cef_util::ParseCommandLineArgs(::GetCommandLineW());
|
||||||
|
if (command_line.size() <= 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cef_util::GetCommandLineValue(command_line, kProcessTypeW).empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& dll_path =
|
||||||
|
cef_util::GetCommandLineValue(command_line, switches::kLibcefPathW);
|
||||||
|
if (dll_path.empty()) {
|
||||||
|
// Default is libcef.dll in the same directory the executable and loaded by
|
||||||
|
// the delayload helper.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_ = Load(dll_path, nullptr, true, true);
|
||||||
|
return handle_ != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
CefScopedLibraryLoader::~CefScopedLibraryLoader() {
|
||||||
|
if (handle_) {
|
||||||
|
::FreeLibrary(handle_);
|
||||||
|
}
|
||||||
|
}
|
142
libcef_dll/wrapper/cef_util_win.cc
Normal file
142
libcef_dll/wrapper/cef_util_win.cc
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
// Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
|
||||||
|
// reserved. Use of this source code is governed by a BSD-style license that
|
||||||
|
// can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "include/wrapper/cef_util_win.h"
|
||||||
|
|
||||||
|
#include <shellapi.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cwctype>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#if defined(CEF_BUILD_BOOTSTRAP)
|
||||||
|
#include "base/check_op.h"
|
||||||
|
#else
|
||||||
|
#include "include/base/cef_logging.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace cef_util {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void TrimWhitespace(std::wstring& str) {
|
||||||
|
// Trim leading whitespace
|
||||||
|
str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](auto ch) {
|
||||||
|
return !std::iswspace(ch);
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Trim trailing whitespace
|
||||||
|
str.erase(std::find_if(str.rbegin(), str.rend(),
|
||||||
|
[](auto ch) { return !std::iswspace(ch); })
|
||||||
|
.base(),
|
||||||
|
str.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
std::wstring GetExePath() {
|
||||||
|
HMODULE hModule = ::GetModuleHandle(nullptr);
|
||||||
|
CHECK(hModule);
|
||||||
|
return GetModulePath(hModule);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring GetModulePath(HMODULE module) {
|
||||||
|
wchar_t buffer[MAX_PATH];
|
||||||
|
DWORD length = ::GetModuleFileName(module, buffer, MAX_PATH);
|
||||||
|
CHECK_NE(length, 0U);
|
||||||
|
CHECK_LT(length, static_cast<DWORD>(MAX_PATH));
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring GetLastErrorAsString() {
|
||||||
|
std::wstring error_message;
|
||||||
|
|
||||||
|
DWORD error_message_id = ::GetLastError();
|
||||||
|
if (error_message_id == 0) {
|
||||||
|
return error_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
LPWSTR message_buffer = NULL;
|
||||||
|
|
||||||
|
DWORD size = FormatMessage(
|
||||||
|
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
||||||
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
NULL, error_message_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
(LPWSTR)&message_buffer, 0, NULL);
|
||||||
|
|
||||||
|
if (message_buffer) {
|
||||||
|
error_message = std::wstring(message_buffer, size);
|
||||||
|
LocalFree(message_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return error_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementation based on CommandLine::ParseFromString.
|
||||||
|
std::vector<std::wstring> ParseCommandLineArgs(const wchar_t* str) {
|
||||||
|
std::wstring command_line = str;
|
||||||
|
TrimWhitespace(command_line);
|
||||||
|
|
||||||
|
int num_args = 0;
|
||||||
|
wchar_t** args = NULL;
|
||||||
|
// When calling CommandLineToArgvW, use the apiset if available.
|
||||||
|
// Doing so will bypass loading shell32.dll on Windows.
|
||||||
|
HMODULE downlevel_shell32_dll =
|
||||||
|
::LoadLibraryEx(L"api-ms-win-downlevel-shell32-l1-1-0.dll", nullptr,
|
||||||
|
LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||||
|
if (downlevel_shell32_dll) {
|
||||||
|
auto command_line_to_argv_w_proc =
|
||||||
|
reinterpret_cast<decltype(::CommandLineToArgvW)*>(
|
||||||
|
::GetProcAddress(downlevel_shell32_dll, "CommandLineToArgvW"));
|
||||||
|
if (command_line_to_argv_w_proc) {
|
||||||
|
args = command_line_to_argv_w_proc(command_line.data(), &num_args);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Since the apiset is not available, allow the delayload of shell32.dll
|
||||||
|
// to take place.
|
||||||
|
args = ::CommandLineToArgvW(command_line.data(), &num_args);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::wstring> result;
|
||||||
|
result.reserve(num_args);
|
||||||
|
|
||||||
|
for (int i = 0; i < num_args; ++i) {
|
||||||
|
std::wstring arg = args[i];
|
||||||
|
TrimWhitespace(arg);
|
||||||
|
if (!arg.empty()) {
|
||||||
|
result.push_back(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalFree(args);
|
||||||
|
|
||||||
|
if (downlevel_shell32_dll) {
|
||||||
|
::FreeLibrary(downlevel_shell32_dll);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring GetCommandLineValue(const std::vector<std::wstring>& command_line,
|
||||||
|
const std::wstring& name) {
|
||||||
|
constexpr wchar_t kPrefix[] = L"--";
|
||||||
|
constexpr wchar_t kSeparator[] = L"=";
|
||||||
|
constexpr wchar_t kQuoteChar = L'"';
|
||||||
|
|
||||||
|
const std::wstring& start = kPrefix + name + kSeparator;
|
||||||
|
for (const auto& arg : command_line) {
|
||||||
|
if (arg.find(start) == 0) {
|
||||||
|
auto value = arg.substr(start.length());
|
||||||
|
if (value.length() > 2 && value[0] == kQuoteChar &&
|
||||||
|
value[arg.length() - 1] == kQuoteChar) {
|
||||||
|
value = value.substr(1, value.length() - 2);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::wstring();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace cef_util
|
@ -4,10 +4,14 @@
|
|||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "include/cef_command_line.h"
|
#include "include/cef_command_line.h"
|
||||||
#include "include/cef_sandbox_win.h"
|
#include "include/cef_sandbox_win.h"
|
||||||
|
#include "include/wrapper/cef_certificate_util_win.h"
|
||||||
|
#include "include/wrapper/cef_library_loader.h"
|
||||||
|
#include "include/wrapper/cef_util_win.h"
|
||||||
#include "tests/cefclient/browser/main_context_impl.h"
|
#include "tests/cefclient/browser/main_context_impl.h"
|
||||||
#include "tests/cefclient/browser/main_message_loop_multithreaded_win.h"
|
#include "tests/cefclient/browser/main_message_loop_multithreaded_win.h"
|
||||||
#include "tests/cefclient/browser/resource.h"
|
#include "tests/cefclient/browser/resource.h"
|
||||||
@ -16,6 +20,7 @@
|
|||||||
#include "tests/shared/browser/client_app_browser.h"
|
#include "tests/shared/browser/client_app_browser.h"
|
||||||
#include "tests/shared/browser/main_message_loop_external_pump.h"
|
#include "tests/shared/browser/main_message_loop_external_pump.h"
|
||||||
#include "tests/shared/browser/main_message_loop_std.h"
|
#include "tests/shared/browser/main_message_loop_std.h"
|
||||||
|
#include "tests/shared/browser/util_win.h"
|
||||||
#include "tests/shared/common/client_app_other.h"
|
#include "tests/shared/common/client_app_other.h"
|
||||||
#include "tests/shared/common/client_switches.h"
|
#include "tests/shared/common/client_switches.h"
|
||||||
#include "tests/shared/renderer/client_app_renderer.h"
|
#include "tests/shared/renderer/client_app_renderer.h"
|
||||||
@ -23,9 +28,101 @@
|
|||||||
namespace client {
|
namespace client {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
// Configure code signing requirements. For a code signing example see
|
||||||
|
// https://github.com/chromiumembedded/cef/issues/3824#issuecomment-2892139995
|
||||||
|
|
||||||
|
// TODO(client): Optionally require that the primary certificate match a
|
||||||
|
// specific thumbprint by setting this value to the SHA1 hash (e.g. 40 character
|
||||||
|
// upper-case hex-encoded value). If this valus is empty and |kAllowUnsigned| is
|
||||||
|
// false then any valid signature will be allowed. This is the "Thumbprint"
|
||||||
|
// output reported by some Windows PowerShell commands. It can also be retrieved
|
||||||
|
// directly with a PowerShell command like: > (Get-ChildItem
|
||||||
|
// Cert:\CurrentUser\My -CodeSigningCert)[0].Thumbprint
|
||||||
|
constexpr char kRequiredThumbprint[] = "";
|
||||||
|
|
||||||
|
// TODO(client): Optionally disallow unsigned binaries by setting this value to
|
||||||
|
// false. This value is disregarded if |kRequiredThumbprint| is specified.
|
||||||
|
constexpr bool kAllowUnsigned = true;
|
||||||
|
|
||||||
|
// TODO(client): Optionally require that all binaries be signed with the same
|
||||||
|
// primary thumbprint. This value is ignored when |kRequiredThumbprint| is
|
||||||
|
// specified or if |kAllowUnsigned| is true.
|
||||||
|
constexpr bool kRequireMatchingThumbprints = false;
|
||||||
|
|
||||||
|
static_assert(sizeof(kRequiredThumbprint) == 1 ||
|
||||||
|
sizeof(kRequiredThumbprint) ==
|
||||||
|
cef_certificate_util::kThumbprintLength + 1,
|
||||||
|
"invalid size for kRequiredThumbprint");
|
||||||
|
|
||||||
|
const char* RequiredThumbprint(std::string* exe_thumbprint) {
|
||||||
|
if constexpr (sizeof(kRequiredThumbprint) ==
|
||||||
|
cef_certificate_util::kThumbprintLength + 1) {
|
||||||
|
return kRequiredThumbprint;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!kAllowUnsigned && kRequireMatchingThumbprints && exe_thumbprint &&
|
||||||
|
exe_thumbprint->length() == cef_certificate_util::kThumbprintLength) {
|
||||||
|
return exe_thumbprint->c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VerifyCodeSigningAndLoad(CefScopedLibraryLoader& library_loader) {
|
||||||
|
// Enable early logging support (required before libcef is loaded).
|
||||||
|
// The *Assert() calls below will output a FATAL error and crash on failure.
|
||||||
|
cef::logging::ScopedEarlySupport scoped_logging({});
|
||||||
|
|
||||||
|
if (library_loader.LoadInSubProcessAssert()) {
|
||||||
|
// Running as a sub-process. We may be sandboxed. Nothing more to be done.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string exe_thumbprint;
|
||||||
|
|
||||||
|
// Check signatures for the already loaded executable. This may be the
|
||||||
|
// bootstrap, or the client executable if not using the bootstrap.
|
||||||
|
const std::wstring& exe_path = cef_util::GetExePath();
|
||||||
|
cef_certificate_util::ThumbprintsInfo exe_info;
|
||||||
|
cef_certificate_util::ValidateCodeSigningAssert(
|
||||||
|
exe_path, RequiredThumbprint(nullptr), kAllowUnsigned, &exe_info);
|
||||||
|
if (exe_info.IsSignedAndValid()) {
|
||||||
|
exe_thumbprint = exe_info.valid_thumbprints[0];
|
||||||
|
CHECK_EQ(cef_certificate_util::kThumbprintLength, exe_thumbprint.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CEF_USE_BOOTSTRAP)
|
||||||
|
// Using a separate bootstrap executable that loaded a client DLL. Check
|
||||||
|
// signatures for the already loaded client DLL.
|
||||||
|
const std::wstring& client_dll_path =
|
||||||
|
cef_util::GetModulePath(client::GetCodeModuleHandle());
|
||||||
|
cef_certificate_util::ValidateCodeSigningAssert(
|
||||||
|
client_dll_path, RequiredThumbprint(&exe_thumbprint), kAllowUnsigned);
|
||||||
|
#endif // defined(CEF_USE_BOOTSTRAP)
|
||||||
|
|
||||||
|
// Require libcef.dll in the same directory as the executable.
|
||||||
|
auto sep_pos = exe_path.find_last_of(L"/\\");
|
||||||
|
CHECK(sep_pos != std::wstring::npos);
|
||||||
|
const auto& libcef_dll_path = exe_path.substr(0, sep_pos + 1) + L"libcef.dll";
|
||||||
|
|
||||||
|
// Validate code signing requirements for libcef.dll before loading, and
|
||||||
|
// then load.
|
||||||
|
return library_loader.LoadInMainAssert(libcef_dll_path.c_str(),
|
||||||
|
RequiredThumbprint(&exe_thumbprint),
|
||||||
|
kAllowUnsigned);
|
||||||
|
}
|
||||||
|
|
||||||
int RunMain(HINSTANCE hInstance, int nCmdShow, void* sandbox_info) {
|
int RunMain(HINSTANCE hInstance, int nCmdShow, void* sandbox_info) {
|
||||||
CefMainArgs main_args(hInstance);
|
CefMainArgs main_args(hInstance);
|
||||||
|
|
||||||
|
// Dynamically load the CEF library after code signing verification.
|
||||||
|
CefScopedLibraryLoader library_loader;
|
||||||
|
if (!VerifyCodeSigningAndLoad(library_loader)) {
|
||||||
|
return CEF_RESULT_CODE_KILLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The CEF library (libcef) is loaded at this point.
|
||||||
|
|
||||||
// Parse command-line arguments.
|
// Parse command-line arguments.
|
||||||
CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine();
|
CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine();
|
||||||
command_line->InitFromString(::GetCommandLineW());
|
command_line->InitFromString(::GetCommandLineW());
|
||||||
|
@ -1120,6 +1120,12 @@ elif platform == 'windows':
|
|||||||
'include/', include_dir, options.quiet)
|
'include/', include_dir, options.quiet)
|
||||||
transfer_gypi_files(cef_dir, cef_paths2['includes_win_capi'], \
|
transfer_gypi_files(cef_dir, cef_paths2['includes_win_capi'], \
|
||||||
'include/', include_dir, options.quiet)
|
'include/', include_dir, options.quiet)
|
||||||
|
transfer_gypi_files(cef_dir, cef_paths2['includes_wrapper_win'], \
|
||||||
|
'include/', include_dir, options.quiet)
|
||||||
|
|
||||||
|
# transfer libcef_dll_wrapper files
|
||||||
|
transfer_gypi_files(cef_dir, cef_paths2['libcef_dll_wrapper_sources_win'], \
|
||||||
|
'libcef_dll/', libcef_dll_dir, options.quiet)
|
||||||
|
|
||||||
# transfer additional files, if any
|
# transfer additional files, if any
|
||||||
transfer_files(cef_dir, script_dir, os.path.join(script_dir, 'distrib', 'win'), \
|
transfer_files(cef_dir, script_dir, os.path.join(script_dir, 'distrib', 'win'), \
|
||||||
@ -1261,7 +1267,7 @@ elif platform == 'mac':
|
|||||||
|
|
||||||
# transfer libcef_dll_wrapper files
|
# transfer libcef_dll_wrapper files
|
||||||
transfer_gypi_files(cef_dir, cef_paths2['libcef_dll_wrapper_sources_mac'], \
|
transfer_gypi_files(cef_dir, cef_paths2['libcef_dll_wrapper_sources_mac'], \
|
||||||
'libcef_dll/', libcef_dll_dir, options.quiet)
|
'libcef_dll/', libcef_dll_dir, options.quiet)
|
||||||
|
|
||||||
# transfer additional files, if any
|
# transfer additional files, if any
|
||||||
transfer_files(cef_dir, script_dir, os.path.join(script_dir, 'distrib', 'mac'), \
|
transfer_files(cef_dir, script_dir, os.path.join(script_dir, 'distrib', 'mac'), \
|
||||||
|
Reference in New Issue
Block a user