Skip to content

Commit

Permalink
windows: fix accept/deny prompt on Windows
Browse files Browse the repository at this point in the history
Launching the browser is prohibited from a Windows
service. `WTSSendMessageA` seems to be the only thing that works. See
comments in the commit.
  • Loading branch information
benma committed Aug 28, 2024
1 parent 948a261 commit 8e99ff4
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 32 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## 1.6.1
- Fix accept/deny prompt on Windows

## 1.6.0

- Prompt to accept/deny a host which is not explicitly allowed
Expand Down
68 changes: 39 additions & 29 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion bitbox-bridge/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "bitbox-bridge"
# If you bump this, also change the ProductCode in bitbox-bridge/release/windows/wix/Product.wxs.
version = "1.6.0"
version = "1.6.1"
authors = ["Niklas Claesson <nicke.claesson@gmail.com>"]
edition = "2021"
license = "Apache-2.0"
Expand All @@ -22,6 +22,9 @@ warp = "0.3.7"
tera = "1.20"
uuid = { version = "1.10.0", features = ["v4"] }

[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.59.0", features = ["Win32", "Win32_System", "Win32_UI", "Win32_System_RemoteDesktop", "Win32_UI_WindowsAndMessaging"] }

[dependencies.u2fframing]
version = "0.1"
path = "../u2fframing"
Expand Down
2 changes: 1 addition & 1 deletion bitbox-bridge/release/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ release-linux:
docker run --user ${USER} --interactive --tty -v ${MOUNT} -w /bitbox-bridge ${CONTAINER} bitbox-bridge/release/linux/release.sh

release-windows:
docker run --user ${USER} --interactive --tty -v ${MOUNT} -w /bitbox-bridge ${CONTAINER} bitbox-bridge/release/windows/release.sh
docker run --interactive --tty -v ${MOUNT} -w /bitbox-bridge ${CONTAINER} bitbox-bridge/release/windows/release.sh

release-darwin:
docker run --user ${USER} --interactive --tty -v ${MOUNT} -w /bitbox-bridge ${CONTAINER} bitbox-bridge/release/darwin/release.sh
Expand Down
2 changes: 1 addition & 1 deletion bitbox-bridge/release/windows/wix/Product.wxs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">

<!-- ProductCode should change with every release -->
<?define ProductCode = "{de37ff56-5490-4e50-9fcb-5bb8e8597544}"?>
<?define ProductCode = "{3fe2b668-f725-4884-92e9-7af6e513ccd9}"?>
<!-- UpgradeCode should stay the same foverever (this is the real ID of the app)-->
<?define UpgradeCode = "{dfe7eecb-5dc0-4c30-ba78-a9eff36efa31}"?>

Expand Down
61 changes: 61 additions & 0 deletions bitbox-bridge/src/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,67 @@ impl AllowedHosts {
}
}

// On windows, we use a native system dialog to prompt the user.
// The browser based prompt does not work as we can't launch a browser from the service (not allowed).
//
// We use WTSSendMessageA as officially recommended:
//
// See https://learn.microsoft.com/en-us/windows/win32/services/interactive-services
// > You can use the following techniques to interact with the user from a service on all supported versions of Windows:
// > Display a dialog box in the user's session using the [WTSSendMessage](https://learn.microsoft.com/en-us/windows/win32/api/wtsapi32/nf-wtsapi32-wtssendmessagea) function.
#[cfg(target_os = "windows")]
async fn user_confirm(
_confirm_state: Arc<ConfirmState>,
message: String,
_base_url: &str,
) -> Result<bool, ()> {
use windows_sys::Win32::System::RemoteDesktop::{
WTSGetActiveConsoleSessionId, WTSSendMessageA, WTS_CURRENT_SERVER_HANDLE,
};

use windows_sys::Win32::UI::WindowsAndMessaging::{IDYES, MB_YESNO, MESSAGEBOX_RESULT};
let title_c = c"BitBoxBridge";
let message_c = std::ffi::CString::new(message.as_str()).map_err(|_| ())?;

let mut response: MESSAGEBOX_RESULT = 0;
let timeout: u32 = 60; // 60 seconds

unsafe {
// Need the active user session ID so the dialog is shown to the user.
let current_session = WTSGetActiveConsoleSessionId();
if current_session == 0xFFFFFFFF {
// See https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-wtsgetactiveconsolesessionid#return-value
return Err(());
}
let result = WTSSendMessageA(
WTS_CURRENT_SERVER_HANDLE,
current_session,
title_c.as_ptr() as *const u8,
title_c.count_bytes() as u32,
message_c.as_ptr() as *const u8,
message_c.as_c_str().count_bytes() as u32,
MB_YESNO,
timeout,
&mut response,
1,
);

if result == 0 {
return Err(());
}
}

// Check if the user clicked 'Yes'
Ok(response == IDYES)
}

// On Linux/maCOS, we launch a browser with a prompt.
//
// On macOS, native dialogs don't work if there is no main window (we don't have one, it's a service).
//
// On linux, native dialogs would work, but we use the browser based solution here too as native
// dialogs might not work in all distros/configs.
#[cfg(not(target_os = "windows"))]
async fn user_confirm(
confirm_state: Arc<ConfirmState>,
message: String,
Expand Down

0 comments on commit 8e99ff4

Please sign in to comment.