#![cfg(windows)]
use std::ffi::c_void;
use windows_sys::Win32::Foundation::{CloseHandle, LocalFree, HANDLE};
use windows_sys::Win32::Security::Authorization::{
ConvertSidToStringSidW, ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1,
};
use windows_sys::Win32::Security::{
GetTokenInformation, TokenUser, SECURITY_ATTRIBUTES, TOKEN_QUERY, TOKEN_USER,
};
use windows_sys::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken};
pub fn pipe_name() -> String {
if let Ok(name) = std::env::var("SCRT4_PIPE_NAME") {
if name.starts_with(r"\\.\pipe\") {
return name;
}
return format!(r"\\.\pipe\{}", name);
}
let user = std::env::var("USERNAME").unwrap_or_else(|_| "default".to_string());
format!(r"\\.\pipe\scrt4-{}", user)
}
fn current_user_sid_string() -> Result<String, String> {
unsafe {
let mut token: HANDLE = std::ptr::null_mut();
if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token) == 0 {
return Err("OpenProcessToken failed".to_string());
}
let mut len: u32 = 0;
GetTokenInformation(token, TokenUser, std::ptr::null_mut(), 0, &mut len);
if len == 0 {
CloseHandle(token);
return Err("GetTokenInformation size query failed".to_string());
}
let mut buf = vec![0u8; len as usize];
let ok = GetTokenInformation(token, TokenUser, buf.as_mut_ptr() as *mut c_void, len, &mut len);
CloseHandle(token);
if ok == 0 {
return Err("GetTokenInformation failed".to_string());
}
let token_user = &*(buf.as_ptr() as *const TOKEN_USER);
let mut sid_ptr: *mut u16 = std::ptr::null_mut();
if ConvertSidToStringSidW(token_user.User.Sid, &mut sid_ptr) == 0 {
return Err("ConvertSidToStringSidW failed".to_string());
}
let mut n = 0usize;
while *sid_ptr.add(n) != 0 {
n += 1;
}
let sid = String::from_utf16_lossy(std::slice::from_raw_parts(sid_ptr, n));
LocalFree(sid_ptr as *mut c_void);
Ok(sid)
}
}
struct OwnerOnlySecurity {
psd: *mut c_void,
sa: SECURITY_ATTRIBUTES,
}
unsafe impl Send for OwnerOnlySecurity {}
impl OwnerOnlySecurity {
fn new() -> Result<Self, String> {
let sid = current_user_sid_string()?;
let sddl = format!("D:P(A;;GA;;;{})(A;;GA;;;SY)", sid);
let sddl_w: Vec<u16> = sddl.encode_utf16().chain(std::iter::once(0)).collect();
let mut psd: *mut c_void = std::ptr::null_mut();
let ok = unsafe {
ConvertStringSecurityDescriptorToSecurityDescriptorW(
sddl_w.as_ptr(),
SDDL_REVISION_1,
&mut psd,
std::ptr::null_mut(),
)
};
if ok == 0 || psd.is_null() {
return Err(format!("Failed to build security descriptor from SDDL: {}", sddl));
}
Ok(Self {
psd,
sa: SECURITY_ATTRIBUTES {
nLength: std::mem::size_of::<SECURITY_ATTRIBUTES>() as u32,
lpSecurityDescriptor: psd,
bInheritHandle: 0,
},
})
}
fn attrs_ptr(&mut self) -> *mut c_void {
&mut self.sa as *mut SECURITY_ATTRIBUTES as *mut c_void
}
}
impl Drop for OwnerOnlySecurity {
fn drop(&mut self) {
unsafe {
LocalFree(self.psd);
}
}
}
pub async fn serve() -> Result<(), Box<dyn std::error::Error>> {
use tokio::net::windows::named_pipe::ServerOptions;
let name = pipe_name();
let mut security = OwnerOnlySecurity::new()?;
let mut server = unsafe {
ServerOptions::new()
.first_pipe_instance(true)
.create_with_security_attributes_raw(&name, security.attrs_ptr())
}?;
tracing::info!("Daemon listening on {}", name);
loop {
if let Err(e) = server.connect().await {
tracing::error!("Pipe connect error: {}", e);
continue;
}
let connected = server;
server = unsafe {
ServerOptions::new().create_with_security_attributes_raw(&name, security.attrs_ptr())
}?;
crate::audit::log_simple(
crate::audit::EventType::ClientConnect,
crate::audit::EventResult::Success,
);
tokio::spawn(crate::handlers::handle_connection(connected));
}
}