mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-01-31 00:35:19 +01:00
fix(bundler): sign DLLs (#11676)
* fix: sign nsis plugin DLLs * also sign DLLs on unix * fix build * create copy of nsis dir * always make a copy of nsis (so linux works, permission error otherwise) * fix windows build * fix * to_path_buf * also create wix copy * remove unused toolset change * fix unused var * fmt * fix wix build * fix build * fix plugin copy * fix conflict * fix file download --------- Co-authored-by: Lucas Nogueira <lucas@tauri.app>
This commit is contained in:
5
.changes/nsis-sign-plugins.md
Normal file
5
.changes/nsis-sign-plugins.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'tauri-bundler': 'patch:enhance'
|
||||
---
|
||||
|
||||
Sign NSIS and WiX DLLs when bundling
|
||||
5
.changes/sign-dlls.md
Normal file
5
.changes/sign-dlls.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'tauri-bundler': 'patch:enhance'
|
||||
---
|
||||
|
||||
Sign DLLs from resources.
|
||||
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -8503,6 +8503,7 @@ dependencies = [
|
||||
"url",
|
||||
"uuid",
|
||||
"walkdir",
|
||||
"which",
|
||||
"windows-registry 0.5.0",
|
||||
"windows-sys 0.59.0",
|
||||
"zip 2.3.0",
|
||||
|
||||
@@ -65,6 +65,9 @@ ar = "0.9"
|
||||
md5 = "0.7"
|
||||
rpm = { version = "0.16", features = ["bzip2-compression"] }
|
||||
|
||||
[target."cfg(unix)".dependencies]
|
||||
which = "7"
|
||||
|
||||
[lib]
|
||||
name = "tauri_bundler"
|
||||
path = "src/lib.rs"
|
||||
|
||||
@@ -472,6 +472,16 @@ pub fn build_wix_app_installer(
|
||||
}
|
||||
fs::create_dir_all(&output_path)?;
|
||||
|
||||
// when we're performing code signing, we'll sign some WiX DLLs, so we make a local copy
|
||||
let wix_toolset_path = if settings.can_sign() {
|
||||
let wix_path = output_path.join("wix");
|
||||
crate::utils::fs_utils::copy_dir(&wix_toolset_path, &wix_path)
|
||||
.context("failed to copy wix directory")?;
|
||||
wix_path
|
||||
} else {
|
||||
wix_toolset_path.to_path_buf()
|
||||
};
|
||||
|
||||
let mut data = BTreeMap::new();
|
||||
|
||||
let silent_webview_install = if let WebviewInstallMode::DownloadBootstrapper { silent }
|
||||
@@ -763,7 +773,11 @@ pub fn build_wix_app_installer(
|
||||
let fragment = fragment_handlebars.render_template(&fragment_content, &data)?;
|
||||
let mut extensions = Vec::new();
|
||||
for cap in extension_regex.captures_iter(&fragment) {
|
||||
extensions.push(wix_toolset_path.join(format!("Wix{}.dll", &cap[1])));
|
||||
let path = wix_toolset_path.join(format!("Wix{}.dll", &cap[1]));
|
||||
if settings.can_sign() {
|
||||
try_sign(&path, settings)?;
|
||||
}
|
||||
extensions.push(path);
|
||||
}
|
||||
candle_inputs.push((fragment_path, extensions));
|
||||
}
|
||||
@@ -773,11 +787,18 @@ pub fn build_wix_app_installer(
|
||||
fragment_extensions.insert(wix_toolset_path.join("WixUIExtension.dll"));
|
||||
fragment_extensions.insert(wix_toolset_path.join("WixUtilExtension.dll"));
|
||||
|
||||
// sign default extensions
|
||||
if settings.can_sign() {
|
||||
for path in &fragment_extensions {
|
||||
try_sign(&path, settings)?;
|
||||
}
|
||||
}
|
||||
|
||||
for (path, extensions) in candle_inputs {
|
||||
for ext in &extensions {
|
||||
fragment_extensions.insert(ext.clone());
|
||||
}
|
||||
run_candle(settings, wix_toolset_path, &output_path, path, extensions)?;
|
||||
run_candle(settings, &wix_toolset_path, &output_path, path, extensions)?;
|
||||
}
|
||||
|
||||
let mut output_paths = Vec::new();
|
||||
@@ -853,7 +874,7 @@ pub fn build_wix_app_installer(
|
||||
log::info!(action = "Running"; "light to produce {}", display_path(&msi_path));
|
||||
|
||||
run_light(
|
||||
wix_toolset_path,
|
||||
&wix_toolset_path,
|
||||
&output_path,
|
||||
arguments,
|
||||
&(fragment_extensions.clone().into_iter().collect()),
|
||||
@@ -968,9 +989,12 @@ fn generate_resource_data(settings: &Settings) -> crate::Result<ResourceMap> {
|
||||
if added_resources.contains(&resource_path) {
|
||||
continue;
|
||||
}
|
||||
|
||||
added_resources.push(resource_path.clone());
|
||||
|
||||
if settings.can_sign() {
|
||||
try_sign(&resource_path, settings)?;
|
||||
}
|
||||
|
||||
let resource_entry = ResourceFile {
|
||||
id: format!("I{}", Uuid::new_v4().as_simple()),
|
||||
guid: Uuid::new_v4().to_string(),
|
||||
@@ -1055,6 +1079,10 @@ fn generate_resource_data(settings: &Settings) -> crate::Result<ResourceMap> {
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
if !added_resources.iter().any(|r| r.ends_with(&relative_path)) {
|
||||
if settings.can_sign() {
|
||||
try_sign(resource_path, settings)?;
|
||||
}
|
||||
|
||||
dlls.push(ResourceFile {
|
||||
id: format!("I{}", Uuid::new_v4().as_simple()),
|
||||
guid: Uuid::new_v4().to_string(),
|
||||
|
||||
@@ -47,7 +47,7 @@ ${StrLoc}
|
||||
!define COPYRIGHT "{{copyright}}"
|
||||
!define OUTFILE "{{out_file}}"
|
||||
!define ARCH "{{arch}}"
|
||||
!define PLUGINSPATH "{{additional_plugins_path}}"
|
||||
!define ADDITIONALPLUGINSPATH "{{additional_plugins_path}}"
|
||||
!define ALLOWDOWNGRADES "{{allow_downgrades}}"
|
||||
!define DISPLAYLANGUAGESELECTOR "{{display_language_selector}}"
|
||||
!define INSTALLWEBVIEW2MODE "{{install_webview2_mode}}"
|
||||
@@ -85,10 +85,8 @@ VIAddVersionKey "LegalCopyright" "${COPYRIGHT}"
|
||||
VIAddVersionKey "FileVersion" "${VERSION}"
|
||||
VIAddVersionKey "ProductVersion" "${VERSION}"
|
||||
|
||||
; Plugins path, currently exists for linux only
|
||||
!if "${PLUGINSPATH}" != ""
|
||||
!addplugindir "${PLUGINSPATH}"
|
||||
!endif
|
||||
# additional plugins
|
||||
!addplugindir "${ADDITIONALPLUGINSPATH}"
|
||||
|
||||
; Uninstaller signing command
|
||||
!if "${UNINSTALLERSIGNCOMMAND}" != ""
|
||||
|
||||
@@ -55,11 +55,18 @@ const NSIS_REQUIRED_FILES: &[&str] = &[
|
||||
"Include/nsDialogs.nsh",
|
||||
"Include/WinMessages.nsh",
|
||||
];
|
||||
const NSIS_PLUGIN_FILES: &[&str] = &[
|
||||
"NSISdl.dll",
|
||||
"StartMenu.dll",
|
||||
"System.dll",
|
||||
"nsDialogs.dll",
|
||||
"additional/nsis_tauri_utils.dll",
|
||||
];
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
const NSIS_REQUIRED_FILES: &[&str] = &["Plugins/x86-unicode/nsis_tauri_utils.dll"];
|
||||
const NSIS_REQUIRED_FILES: &[&str] = &["Plugins/x86-unicode/additional/nsis_tauri_utils.dll"];
|
||||
|
||||
const NSIS_REQUIRED_FILES_HASH: &[(&str, &str, &str, HashAlgorithm)] = &[(
|
||||
"Plugins/x86-unicode/nsis_tauri_utils.dll",
|
||||
"Plugins/x86-unicode/additional/nsis_tauri_utils.dll",
|
||||
NSIS_TAURI_UTILS_URL,
|
||||
NSIS_TAURI_UTILS_SHA1,
|
||||
HashAlgorithm::Sha1,
|
||||
@@ -96,7 +103,10 @@ pub fn bundle_project(settings: &Settings, updater: bool) -> crate::Result<Vec<P
|
||||
log::warn!("NSIS directory contains mis-hashed files. Redownloading them.");
|
||||
for (path, url, hash, hash_algorithm) in mismatched {
|
||||
let data = download_and_verify(url, hash, *hash_algorithm)?;
|
||||
fs::write(nsis_toolset_path.join(path), data)?;
|
||||
let out_path = nsis_toolset_path.join(path);
|
||||
std::fs::create_dir_all(out_path.parent().context("output path has no parent")?)
|
||||
.context("failed to create file output directory")?;
|
||||
fs::write(out_path, data).with_context(|| format!("failed to save {path}"))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,6 +126,7 @@ fn get_and_extract_nsis(nsis_toolset_path: &Path, _tauri_tools_path: &Path) -> c
|
||||
fs::rename(_tauri_tools_path.join("nsis-3.08"), nsis_toolset_path)?;
|
||||
}
|
||||
|
||||
// download additional plugins
|
||||
let nsis_plugins = nsis_toolset_path.join("Plugins");
|
||||
|
||||
let data = download_and_verify(
|
||||
@@ -124,7 +135,7 @@ fn get_and_extract_nsis(nsis_toolset_path: &Path, _tauri_tools_path: &Path) -> c
|
||||
HashAlgorithm::Sha1,
|
||||
)?;
|
||||
|
||||
let target_folder = nsis_plugins.join("x86-unicode");
|
||||
let target_folder = nsis_plugins.join("x86-unicode").join("additional");
|
||||
fs::create_dir_all(&target_folder)?;
|
||||
fs::write(target_folder.join("nsis_tauri_utils.dll"), data)?;
|
||||
|
||||
@@ -156,7 +167,7 @@ fn try_add_numeric_build_number(version_str: &str) -> anyhow::Result<String> {
|
||||
|
||||
fn build_nsis_app_installer(
|
||||
settings: &Settings,
|
||||
_nsis_toolset_path: &Path,
|
||||
#[allow(unused_variables)] nsis_toolset_path: &Path,
|
||||
tauri_tools_path: &Path,
|
||||
updater: bool,
|
||||
) -> crate::Result<Vec<PathBuf>> {
|
||||
@@ -180,6 +191,65 @@ fn build_nsis_app_installer(
|
||||
}
|
||||
fs::create_dir_all(&output_path)?;
|
||||
|
||||
// we make a copy of the NSIS directory if we're going to sign its DLLs
|
||||
// because we don't want to change the DLL hashes so the cache can reuse it
|
||||
let maybe_plugin_copy_path = if settings.can_sign() {
|
||||
// find nsis path
|
||||
#[cfg(target_os = "linux")]
|
||||
let system_nsis_toolset_path = std::env::var_os("NSIS_PATH")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|| PathBuf::from("/usr/share/nsis"));
|
||||
#[cfg(target_os = "macos")]
|
||||
let system_nsis_toolset_path = std::env::var_os("NSIS_PATH")
|
||||
.map(PathBuf::from)
|
||||
.ok_or_else(|| anyhow::anyhow!("failed to resolve NSIS path"))
|
||||
.or_else(|_| {
|
||||
let mut makensis_path =
|
||||
which::which("makensis").context("failed to resolve `makensis`; did you install nsis? See https://tauri.app/distribute/windows-installer/#install-nsis for more information")?;
|
||||
// homebrew installs it as a symlink
|
||||
if makensis_path.is_symlink() {
|
||||
// read_link might return a path relative to makensis_path so we must use join() and canonicalize
|
||||
makensis_path = makensis_path
|
||||
.parent()
|
||||
.context("missing makensis parent")?
|
||||
.join(std::fs::read_link(&makensis_path).context("failed to resolve makensis symlink")?)
|
||||
.canonicalize()
|
||||
.context("failed to resolve makensis path")?;
|
||||
}
|
||||
// file structure:
|
||||
// ├── bin
|
||||
// │ ├── makensis
|
||||
// ├── share
|
||||
// │ ├── nsis
|
||||
let bin_folder = makensis_path.parent().context("missing makensis parent")?;
|
||||
let root_folder = bin_folder.parent().context("missing makensis root")?;
|
||||
crate::Result::Ok(root_folder.join("share").join("nsis"))
|
||||
})?;
|
||||
#[cfg(windows)]
|
||||
let system_nsis_toolset_path = nsis_toolset_path.to_path_buf();
|
||||
|
||||
let plugins_path = output_path.join("Plugins");
|
||||
// copy system plugins (we don't want to modify system installed DLLs, and on some systems there will even be permission errors if we try)
|
||||
crate::utils::fs_utils::copy_dir(
|
||||
&system_nsis_toolset_path.join("Plugins").join("x86-unicode"),
|
||||
&plugins_path.join("x86-unicode"),
|
||||
)
|
||||
.context("failed to copy system NSIS Plugins folder to local copy")?;
|
||||
// copy our downloaded DLLs
|
||||
crate::utils::fs_utils::copy_dir(
|
||||
&nsis_toolset_path
|
||||
.join("Plugins")
|
||||
.join("x86-unicode")
|
||||
.join("additional"),
|
||||
&plugins_path.join("x86-unicode").join("additional"),
|
||||
)
|
||||
.context("failed to copy additional NSIS Plugins folder to local copy")?;
|
||||
Some(plugins_path)
|
||||
} else {
|
||||
// in this case plugin_copy_path can be None, we'll use the system default path
|
||||
None
|
||||
};
|
||||
|
||||
let mut data = BTreeMap::new();
|
||||
|
||||
let bundle_id = settings.bundle_identifier();
|
||||
@@ -187,12 +257,17 @@ fn build_nsis_app_installer(
|
||||
.publisher()
|
||||
.unwrap_or_else(|| bundle_id.split('.').nth(1).unwrap_or(bundle_id));
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
let mut dir = dirs::cache_dir().unwrap();
|
||||
dir.extend(["tauri", "NSIS", "Plugins", "x86-unicode"]);
|
||||
data.insert("additional_plugins_path", to_json(dir));
|
||||
}
|
||||
let additional_plugins_path = maybe_plugin_copy_path
|
||||
.clone()
|
||||
.unwrap_or_else(|| nsis_toolset_path.join("Plugins"))
|
||||
.join("x86-unicode")
|
||||
.join("additional");
|
||||
|
||||
data.insert(
|
||||
"additional_plugins_path",
|
||||
// either our Plugins copy (when signing) or the cache/Plugins/x86-unicode path
|
||||
to_json(&additional_plugins_path),
|
||||
);
|
||||
|
||||
data.insert("arch", to_json(arch));
|
||||
data.insert("bundle_id", to_json(bundle_id));
|
||||
@@ -526,13 +601,29 @@ fn build_nsis_app_installer(
|
||||
));
|
||||
fs::create_dir_all(nsis_installer_path.parent().unwrap())?;
|
||||
|
||||
log::info!(action = "Running"; "makensis.exe to produce {}", display_path(&nsis_installer_path));
|
||||
if settings.can_sign() {
|
||||
log::info!("Signing NSIS plugins");
|
||||
for dll in NSIS_PLUGIN_FILES {
|
||||
let path = additional_plugins_path.join(dll);
|
||||
if path.exists() {
|
||||
try_sign(&path, settings)?;
|
||||
} else {
|
||||
log::warn!("Could not find {}, skipping signing", path.display());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log::info!(action = "Running"; "makensis to produce {}", display_path(&nsis_installer_path));
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
let mut nsis_cmd = Command::new(_nsis_toolset_path.join("makensis.exe"));
|
||||
let mut nsis_cmd = Command::new(nsis_toolset_path.join("makensis.exe"));
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let mut nsis_cmd = Command::new("makensis");
|
||||
|
||||
if let Some(plugins_path) = &maybe_plugin_copy_path {
|
||||
nsis_cmd.env("NSISPLUGINS", plugins_path);
|
||||
}
|
||||
|
||||
nsis_cmd
|
||||
.args(["-INPUTCHARSET", "UTF8", "-OUTPUTCHARSET", "UTF8"])
|
||||
.arg(match settings.log_level() {
|
||||
@@ -628,6 +719,9 @@ fn generate_resource_data(settings: &Settings) -> crate::Result<ResourcesMap> {
|
||||
let loader_path =
|
||||
dunce::simplified(&settings.project_out_directory().join("WebView2Loader.dll")).to_path_buf();
|
||||
if loader_path.exists() {
|
||||
if settings.can_sign() {
|
||||
try_sign(&loader_path, settings)?;
|
||||
}
|
||||
added_resources.push(loader_path.clone());
|
||||
resources.insert(
|
||||
loader_path,
|
||||
@@ -650,6 +744,10 @@ fn generate_resource_data(settings: &Settings) -> crate::Result<ResourcesMap> {
|
||||
}
|
||||
added_resources.push(resource_path.clone());
|
||||
|
||||
if settings.can_sign() {
|
||||
try_sign(&resource_path, settings)?;
|
||||
}
|
||||
|
||||
let target_path = resource.target();
|
||||
resources.insert(
|
||||
resource_path,
|
||||
|
||||
@@ -241,9 +241,9 @@ pub fn sign<P: AsRef<Path>>(path: P, params: &SignParams) -> crate::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_sign(file_path: &std::path::PathBuf, settings: &Settings) -> crate::Result<()> {
|
||||
pub fn try_sign<P: AsRef<Path>>(file_path: P, settings: &Settings) -> crate::Result<()> {
|
||||
if settings.can_sign() {
|
||||
log::info!(action = "Signing"; "{}", tauri_utils::display_path(file_path));
|
||||
log::info!(action = "Signing"; "{}", tauri_utils::display_path(file_path.as_ref()));
|
||||
sign(file_path, &settings.sign_params())?;
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -111,9 +111,6 @@ pub fn copy_dir(from: &Path, to: &Path) -> crate::Result<()> {
|
||||
"{from:?} is not a Directory"
|
||||
)));
|
||||
}
|
||||
if to.exists() {
|
||||
return Err(crate::Error::GenericError(format!("{to:?} already exists")));
|
||||
}
|
||||
let parent = to.parent().expect("No data in parent");
|
||||
fs::create_dir_all(parent)?;
|
||||
for entry in walkdir::WalkDir::new(from) {
|
||||
@@ -129,7 +126,7 @@ pub fn copy_dir(from: &Path, to: &Path) -> crate::Result<()> {
|
||||
symlink_file(&target, &dest_path)?;
|
||||
}
|
||||
} else if entry.file_type().is_dir() {
|
||||
fs::create_dir(dest_path)?;
|
||||
fs::create_dir_all(dest_path)?;
|
||||
} else {
|
||||
fs::copy(entry.path(), dest_path)?;
|
||||
}
|
||||
|
||||
@@ -22,16 +22,20 @@ fn main() {
|
||||
)
|
||||
.expect("failed to run tauri-build");
|
||||
|
||||
// workaround needed to prevent `STATUS_ENTRYPOINT_NOT_FOUND` error in tests
|
||||
// see https://github.com/tauri-apps/tauri/pull/4383#issuecomment-1212221864
|
||||
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
let target_env = std::env::var("CARGO_CFG_TARGET_ENV");
|
||||
let is_tauri_workspace = std::env::var("__TAURI_WORKSPACE__").is_ok_and(|v| v == "true");
|
||||
if is_tauri_workspace && target_os == "windows" && Ok("msvc") == target_env.as_deref() {
|
||||
embed_manifest_for_tests();
|
||||
#[cfg(windows)]
|
||||
{
|
||||
// workaround needed to prevent `STATUS_ENTRYPOINT_NOT_FOUND` error in tests
|
||||
// see https://github.com/tauri-apps/tauri/pull/4383#issuecomment-1212221864
|
||||
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
let target_env = std::env::var("CARGO_CFG_TARGET_ENV");
|
||||
let is_tauri_workspace = std::env::var("__TAURI_WORKSPACE__").is_ok_and(|v| v == "true");
|
||||
if is_tauri_workspace && target_os == "windows" && Ok("msvc") == target_env.as_deref() {
|
||||
embed_manifest_for_tests();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn embed_manifest_for_tests() {
|
||||
static WINDOWS_MANIFEST_FILE: &str = "windows-app-manifest.xml";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user