Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

windows: fix accept/deny prompt on Windows #55

Merged
merged 1 commit into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 <[email protected]>"]
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/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
71 changes: 71 additions & 0 deletions bitbox-bridge/src/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,77 @@ 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 WTSSendMessage as officially recommended (it recommends WTSSendMessageA, the ANSI-encoded
// version, but WTSSendMessagW for UTF-16 also works):
//
// 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 std::os::windows::ffi::OsStrExt;
use windows_sys::Win32::System::RemoteDesktop::{
WTSGetActiveConsoleSessionId, WTSSendMessageW, WTS_CURRENT_SERVER_HANDLE,
};
use windows_sys::Win32::UI::WindowsAndMessaging::{IDYES, MB_YESNO, MESSAGEBOX_RESULT};

fn utf8_to_utf16(s: &str) -> Vec<u16> {
std::ffi::OsStr::new(s)
.encode_wide()
.chain(std::iter::once(0))
.collect()
}

let title_c = utf8_to_utf16("BitBoxBridge");
let message_c = utf8_to_utf16(&message);

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 = WTSSendMessageW(
WTS_CURRENT_SERVER_HANDLE,
current_session,
title_c.as_ptr(),
// Each element is 2 bytes except, including the the null terminator
(title_c.len() * 2) as u32,
message_c.as_ptr(),
(message_c.len() * 2) 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
Loading