Skip to content

Commit 4dd22fc

Browse files
committed
feat(pedm): add version query and forced signature check for JIT elevation
1 parent bee8578 commit 4dd22fc

6 files changed

Lines changed: 80 additions & 2 deletions

File tree

crates/devolutions-pedm-shared/src/policy.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ pub struct Profile {
317317
pub elevation_settings: ElevationConfigurations,
318318
pub default_elevation_kind: ElevationKind,
319319
pub prompt_secure_desktop: bool,
320+
pub target_must_be_signed: bool,
320321
}
321322

322323
impl Identifiable for Profile {

crates/devolutions-pedm/src/api/about.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,6 @@ pub(crate) async fn about(
2121
startup_request_count: state.startup_info.request_count,
2222
current_request_count: state.req_counter.load(Ordering::Relaxed),
2323
last_request_time: db.get_last_request_time().await?,
24+
version: win_api_wrappers::utils::get_exe_version()?,
2425
}))
2526
}

crates/devolutions-pedm/src/api/err.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ impl From<DbError> for HandlerError {
5151
}
5252
}
5353

54+
impl From<anyhow::Error> for HandlerError {
55+
fn from(e: anyhow::Error) -> Self {
56+
Self(StatusCode::INTERNAL_SERVER_ERROR, Some(e.to_string()))
57+
}
58+
}
59+
5460
#[cfg(feature = "postgres")]
5561
impl From<tokio_postgres::Error> for HandlerError {
5662
fn from(e: tokio_postgres::Error) -> Self {

crates/devolutions-pedm/src/model.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub(crate) struct AboutData {
1313
///
1414
/// This can be `None` if `/about` is the first request made.
1515
pub last_request_time: Option<DateTime<Utc>>,
16+
pub version: String,
1617
}
1718

1819
/// Immutable startup info.

crates/devolutions-pedm/src/policy.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ use serde::Serialize;
2222
use tracing::info;
2323
use uuid::Uuid;
2424

25-
use devolutions_pedm_shared::policy;
2625
use devolutions_pedm_shared::policy::{
27-
Application, Certificate, Configuration, ElevationRequest, Identifiable, Profile, Signature, Signer, User,
26+
self, Application, AuthenticodeSignatureStatus, Certificate, Configuration, ElevationRequest, Identifiable,
27+
Profile, Signature, Signer, User,
2828
};
2929
use win_api_wrappers::identity::sid::Sid;
3030
use win_api_wrappers::process::Process;
@@ -333,6 +333,12 @@ impl Policy {
333333
.user_current_profile(&request.asker.user)
334334
.ok_or_else(|| anyhow!(Error::AccessDenied))?;
335335

336+
if profile.target_must_be_signed {
337+
if request.target.signature.status != AuthenticodeSignatureStatus::Valid {
338+
bail!(Error::AccessDenied)
339+
}
340+
}
341+
336342
let elevation_type = profile.default_elevation_kind;
337343

338344
match elevation_type {

crates/win-api-wrappers/src/utils.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,69 @@ pub fn expand_environment_path(src: &Path, environment: &HashMap<String, String>
457457
))?)
458458
}
459459

460+
use windows::Win32::Foundation::{BOOL, HMODULE};
461+
use windows::Win32::Storage::FileSystem::{
462+
GetFileVersionInfoSizeW, GetFileVersionInfoW, VerQueryValueW, VS_FIXEDFILEINFO,
463+
};
464+
use windows::Win32::System::LibraryLoader::{GetModuleFileNameW, GetModuleHandleW};
465+
466+
pub fn get_exe_version() -> Result<String, anyhow::Error> {
467+
// Passing None to GetModuleHandleW requests the handle to the current process main executable module
468+
let h_module: HMODULE = unsafe { GetModuleHandleW(None)? };
469+
470+
let mut path_buf = [0u16; MAX_PATH as usize];
471+
472+
// SAFETY: We're passing a valid mutable buffer to GetModuleFileNameW of large enough size (MAX_PATH WCHARs)
473+
let len = unsafe { GetModuleFileNameW(h_module, &mut path_buf) };
474+
475+
if len == 0 {
476+
anyhow::bail!("GetModuleFileNameW failed: {}", windows::core::Error::from_win32());
477+
}
478+
479+
let exe_path = &path_buf[..len as usize];
480+
let exe_path_w = PCWSTR(exe_path.as_ptr());
481+
482+
// SAFETY: `exe_path_w` is a valid pointer to a null-terminated UTF-16 string from the OS.
483+
let size = unsafe { GetFileVersionInfoSizeW(exe_path_w, None) };
484+
if size == 0 {
485+
anyhow::bail!("GetFileVersionInfoSizeW failed: {}", windows::core::Error::from_win32());
486+
}
487+
488+
let mut buffer = vec![0u8; size as usize];
489+
490+
// SAFETY: `buffer` is allocated with the correct size.
491+
// `exe_path_w` is a valid pointer to a null-terminated UTF-16 string from the OS.
492+
unsafe { GetFileVersionInfoW(exe_path_w, 0, size, buffer.as_mut_ptr() as *mut _)? };
493+
494+
let mut lp_buffer: *mut c_void = ptr::null_mut();
495+
let mut len = 0u32;
496+
let path = "\\\0".encode_utf16().collect::<Vec<u16>>();
497+
let path_ptr = PCWSTR(path.as_ptr());
498+
499+
// SAFETY: The version info buffer is valid and comes from GetFileVersionInfoW.
500+
// The query string is valid null-terminated UTF-16.
501+
// `lp_buffer` and `len` are valid out parameters.
502+
let ok = unsafe { VerQueryValueW(buffer.as_ptr() as *const _, path_ptr, &mut lp_buffer, &mut len) };
503+
504+
if !ok.as_bool() {
505+
anyhow::bail!("VerQueryValueW failed");
506+
}
507+
508+
// SAFETY: ff VerQueryValueW succeeded, `lp_buffer` points to a valid VS_FIXEDFILEINFO.
509+
let info = unsafe { &*(lp_buffer as *const VS_FIXEDFILEINFO) };
510+
511+
if info.dwSignature != 0xFEEF04BD {
512+
bail!("invalid VS_FIXEDFILEINFO signature");
513+
}
514+
515+
let major = (info.dwFileVersionMS >> 16) & 0xffff;
516+
let minor = (info.dwFileVersionMS >> 0) & 0xffff;
517+
let build = (info.dwFileVersionLS >> 16) & 0xffff;
518+
let revision = (info.dwFileVersionLS >> 0) & 0xffff;
519+
520+
Ok(format!("{}.{}.{}.{}", major, minor, build, revision))
521+
}
522+
460523
pub struct Snapshot {
461524
handle: OwnedHandle,
462525
}

0 commit comments

Comments
 (0)