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

implement non-EXE support #89

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
47 changes: 47 additions & 0 deletions sudo/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use windows::Win32::System::Rpc::RPC_STATUS;
use windows::Win32::System::SystemServices::{
IMAGE_DOS_HEADER, IMAGE_DOS_SIGNATURE, IMAGE_NT_SIGNATURE, SE_TOKEN_USER, SE_TOKEN_USER_1,
};
use windows::Win32::UI::Shell::{AssocQueryStringW, ASSOCF, ASSOCSTR_COMMAND};
use windows::{
core::*, Win32::Foundation::*, Win32::Security::Authorization::*, Win32::Security::*,
Win32::System::Console::*, Win32::System::Threading::*,
Expand Down Expand Up @@ -586,6 +587,52 @@ pub fn get_exe_subsystem<P: AsRef<Path>>(path: P) -> Result<IMAGE_SUBSYSTEM> {
Ok(nt.OptionalHeader.Subsystem)
}

// TODO: Improve this logic
pub fn get_default_application<P: AsRef<Path>>(path: P) -> Result<String> {
let ext = path.as_ref()
.extension()
.and_then(|ext| ext.to_str())
.map_or_else(String::new, |ext_str| format!(".{}", ext_str));
let mut buffer_size: u32 = 0;

let h_result = unsafe {
AssocQueryStringW(
ASSOCF::default(),
ASSOCSTR_COMMAND,
PCWSTR(HSTRING::from(ext.clone()).as_ptr()),
PCWSTR::null(),
PWSTR::null(),
&mut buffer_size,
)
};
if h_result.is_err() {
return Err(h_result.into());
}

let mut buffer: Vec<u16> = vec![0; buffer_size as usize];
let h_result = unsafe {
AssocQueryStringW(
ASSOCF::default(),
ASSOCSTR_COMMAND,
PCWSTR(HSTRING::from(ext).as_ptr()),
PCWSTR::null(),
PWSTR(buffer.as_mut_ptr()),
&mut buffer_size,
)
};
if h_result.is_err() {
return Err(h_result.into());
}

// The replace statements account for shell/open/command quirks
let result =
String::from_utf16_lossy(&buffer[..buffer_size as usize - 1])
.replace("\"%1\"", "")
.replace("%1", "");

Ok(result)
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
18 changes: 11 additions & 7 deletions sudo/src/run_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ fn adjust_args_for_intrinsics_and_cmdlets(req: &mut ElevateRequest) -> Result<bo
Ok(false)
}

fn adjust_args_for_gui_exes(req: &mut ElevateRequest) {
fn adjust_args_for_files(req: &mut ElevateRequest) {
// We did find the command. We're now gonna try to find out if the file
// is:
// - An command line exe
Expand All @@ -169,15 +169,19 @@ fn adjust_args_for_gui_exes(req: &mut ElevateRequest) {
tracing::trace_log_message(&format!("is_exe: {is_exe}"));
tracing::trace_log_message(&format!("is_gui: {is_gui}"));

// TODO: figure out how to handle non-exe files. ShellExecute(runas,
// ...) doesn't do anything for them, and I'm not sure we can trivially
// have the service find out what the right verb is for an arbitrary
// extension. (this is the kind of comment I'm sure to be proven wrong
// about)
if is_gui {
tracing::trace_log_message("not cli exe. Force new window");
req.sudo_mode = SudoMode::ForceNewWindow;
}

// We're changing the argument to the application here and using the
// registry to figure out the default application for the specified
// file.
if !is_exe {
req.args
.splice(0..0, [req.application.clone()]);
req.application = get_default_application(&req.application).expect("Failed to get the default application");
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this might fail if a file doesn't have a file association with it. the standard behavior (non-sudo) is to open the open with prompt, so this might have to be changed before being merged

}
}

pub fn run_target(
Expand Down Expand Up @@ -272,7 +276,7 @@ fn prepare_request(
// found here in the unelevated context.

req.application = absolute_path(&path)?.to_string_lossy().to_string();
adjust_args_for_gui_exes(&mut req);
adjust_args_for_files(&mut req);
} else {
tracing::trace_command_not_found(&req.application);

Expand Down