Skip to content

Commit 4d270a9

Browse files
acx0Legend-Master
andauthored
fix(windows): patch_binary causing codesigning verification failure (#13943)
* fix(windows): `patch_binary` causing codesigning verification failure * `cargo fmt` * add change file * Update .changes/fix-binary-patching-codesign-verification-failure.md --------- Co-authored-by: Tony <[email protected]>
1 parent bcc7a82 commit 4d270a9

File tree

3 files changed

+42
-6
lines changed

3 files changed

+42
-6
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'tauri-cli': 'patch:bug'
3+
'@tauri-apps/cli': 'patch:bug'
4+
---
5+
6+
Fix codesigning verification failures caused by binary-patching during bundling

crates/tauri-bundler/src/bundle.rs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,11 @@ pub use self::{
5353
use anyhow::Context;
5454
pub use settings::{NsisSettings, WindowsSettings, WixLanguage, WixLanguageConfig, WixSettings};
5555

56-
use std::{fmt::Write, path::PathBuf};
56+
use std::{
57+
fmt::Write,
58+
io::{Seek, SeekFrom},
59+
path::PathBuf,
60+
};
5761

5862
/// Generated bundle metadata.
5963
#[derive(Debug)]
@@ -122,22 +126,46 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
122126
.iter()
123127
.find(|b| b.main())
124128
.expect("Main binary missing in settings");
129+
let main_binary_path = settings.binary_path(main_binary);
130+
131+
// When packaging multiple binary types, we make a copy of the unsigned main_binary so that we can
132+
// restore it after each package_type step. This avoids two issues:
133+
// - modifying a signed binary without updating its PE checksum can break signature verification
134+
// - codesigning tools should handle calculating+updating this, we just need to ensure
135+
// (re)signing is performed after every `patch_binary()` operation
136+
// - signing an already-signed binary can result in multiple signatures, causing verification errors
137+
let main_binary_reset_required =
138+
matches!(target_os, TargetPlatform::Windows) && settings.can_sign() && package_types.len() > 1;
139+
let mut unsigned_main_binary_copy = tempfile::tempfile()?;
140+
if main_binary_reset_required {
141+
let mut unsigned_main_binary = std::fs::File::open(&main_binary_path)?;
142+
std::io::copy(&mut unsigned_main_binary, &mut unsigned_main_binary_copy)?;
143+
}
125144

145+
let mut main_binary_signed = false;
126146
let mut bundles = Vec::<Bundle>::new();
127147
for package_type in &package_types {
128148
// bundle was already built! e.g. DMG already built .app
129149
if bundles.iter().any(|b| b.package_type == *package_type) {
130150
continue;
131151
}
132152

133-
if let Err(e) = patch_binary(&settings.binary_path(main_binary), package_type) {
153+
if let Err(e) = patch_binary(&main_binary_path, package_type) {
134154
log::warn!("Failed to add bundler type to the binary: {e}. Updater plugin may not be able to update this package. This shouldn't normally happen, please report it to https://github.com/tauri-apps/tauri/issues");
135155
}
136156

137157
// sign main binary for every package type after patch
138158
if matches!(target_os, TargetPlatform::Windows) && settings.can_sign() {
139-
let bin_path = settings.binary_path(main_binary);
140-
windows::sign::try_sign(&bin_path, settings)?;
159+
if main_binary_signed && main_binary_reset_required {
160+
let mut signed_main_binary = std::fs::OpenOptions::new()
161+
.write(true)
162+
.truncate(true)
163+
.open(&main_binary_path)?;
164+
unsigned_main_binary_copy.seek(SeekFrom::Start(0))?;
165+
std::io::copy(&mut unsigned_main_binary_copy, &mut signed_main_binary)?;
166+
}
167+
windows::sign::try_sign(&main_binary_path, settings)?;
168+
main_binary_signed = true;
141169
}
142170

143171
let bundle_paths = match package_type {
@@ -160,6 +188,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
160188

161189
#[cfg(target_os = "windows")]
162190
PackageType::WindowsMsi => windows::msi::bundle_project(settings, false)?,
191+
// note: don't restrict to windows as NSIS installers can be built in linux using cargo-xwin
163192
PackageType::Nsis => windows::nsis::bundle_project(settings, false)?,
164193

165194
#[cfg(target_os = "linux")]

crates/tauri-bundler/src/bundle/windows/util.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,7 @@ pub fn os_bitness<'a>() -> Option<&'a str> {
8585
}
8686

8787
pub fn patch_binary(binary_path: &PathBuf, package_type: &crate::PackageType) -> crate::Result<()> {
88-
let file_data = std::fs::read(binary_path)?;
89-
let mut file_data = file_data; // make mutable
88+
let mut file_data = std::fs::read(binary_path)?;
9089

9190
let pe = match goblin::Object::parse(&file_data)? {
9291
goblin::Object::PE(pe) => pe,
@@ -132,6 +131,8 @@ pub fn patch_binary(binary_path: &PathBuf, package_type: &crate::PackageType) ->
132131
)
133132
})?;
134133

134+
// see "Relative virtual address (RVA)" for explanation of offset arithmetic here:
135+
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#general-concepts
135136
let file_offset = rdata_section.pointer_to_raw_data as usize
136137
+ (rva as usize).saturating_sub(rdata_section.virtual_address as usize);
137138

0 commit comments

Comments
 (0)