diff --git a/agent/Cargo.toml b/agent/Cargo.toml index 25cfbd5..ca24f6b 100644 --- a/agent/Cargo.toml +++ b/agent/Cargo.toml @@ -77,6 +77,7 @@ windows = { version = "0.58", features = [ "Win32_Storage_FileSystem", "Win32_System_Pipes", "Win32_System_SystemServices", + "Win32_System_IO", ]} # Windows service support diff --git a/agent/src/bin/sas_service.rs b/agent/src/bin/sas_service.rs index f86aa4f..23e2c8f 100644 --- a/agent/src/bin/sas_service.rs +++ b/agent/src/bin/sas_service.rs @@ -4,26 +4,13 @@ //! The agent communicates with this service via named pipe IPC. use std::ffi::OsString; -use std::io::{Read, Write}; +use std::io::{Read, Write as IoWrite}; use std::sync::mpsc; use std::time::Duration; use anyhow::{Context, Result}; -use windows::core::{s, w, PCSTR}; -use windows::Win32::Foundation::{CloseHandle, HANDLE, INVALID_HANDLE_VALUE}; -use windows::Win32::Security::{ - InitializeSecurityDescriptor, SetSecurityDescriptorDacl, PSECURITY_DESCRIPTOR, - SECURITY_ATTRIBUTES, SECURITY_DESCRIPTOR, -}; -use windows::Win32::Storage::FileSystem::{ - FlushFileBuffers, ReadFile, WriteFile, -}; +use windows::core::{s, w}; use windows::Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryW}; -use windows::Win32::System::Pipes::{ - ConnectNamedPipe, CreateNamedPipeW, DisconnectNamedPipe, PIPE_ACCESS_DUPLEX, - PIPE_READMODE_MESSAGE, PIPE_TYPE_MESSAGE, PIPE_UNLIMITED_INSTANCES, PIPE_WAIT, -}; -use windows::Win32::System::SystemServices::SECURITY_DESCRIPTOR_REVISION; use windows_service::{ define_windows_service, service::{ @@ -42,6 +29,67 @@ const SERVICE_DESCRIPTION: &str = "Handles Secure Attention Sequence (Ctrl+Alt+D const PIPE_NAME: &str = r"\\.\pipe\guruconnect-sas"; const INSTALL_DIR: &str = r"C:\Program Files\GuruConnect"; +// Windows named pipe constants +const PIPE_ACCESS_DUPLEX: u32 = 0x00000003; +const PIPE_TYPE_MESSAGE: u32 = 0x00000004; +const PIPE_READMODE_MESSAGE: u32 = 0x00000002; +const PIPE_WAIT: u32 = 0x00000000; +const PIPE_UNLIMITED_INSTANCES: u32 = 255; +const INVALID_HANDLE_VALUE: isize = -1; +const SECURITY_DESCRIPTOR_REVISION: u32 = 1; + +// FFI declarations for named pipe operations +#[link(name = "kernel32")] +extern "system" { + fn CreateNamedPipeW( + lpName: *const u16, + dwOpenMode: u32, + dwPipeMode: u32, + nMaxInstances: u32, + nOutBufferSize: u32, + nInBufferSize: u32, + nDefaultTimeOut: u32, + lpSecurityAttributes: *mut SECURITY_ATTRIBUTES, + ) -> isize; + + fn ConnectNamedPipe(hNamedPipe: isize, lpOverlapped: *mut std::ffi::c_void) -> i32; + fn DisconnectNamedPipe(hNamedPipe: isize) -> i32; + fn CloseHandle(hObject: isize) -> i32; + fn ReadFile( + hFile: isize, + lpBuffer: *mut u8, + nNumberOfBytesToRead: u32, + lpNumberOfBytesRead: *mut u32, + lpOverlapped: *mut std::ffi::c_void, + ) -> i32; + fn WriteFile( + hFile: isize, + lpBuffer: *const u8, + nNumberOfBytesToWrite: u32, + lpNumberOfBytesWritten: *mut u32, + lpOverlapped: *mut std::ffi::c_void, + ) -> i32; + fn FlushFileBuffers(hFile: isize) -> i32; +} + +#[link(name = "advapi32")] +extern "system" { + fn InitializeSecurityDescriptor(pSecurityDescriptor: *mut u8, dwRevision: u32) -> i32; + fn SetSecurityDescriptorDacl( + pSecurityDescriptor: *mut u8, + bDaclPresent: i32, + pDacl: *mut std::ffi::c_void, + bDaclDefaulted: i32, + ) -> i32; +} + +#[repr(C)] +struct SECURITY_ATTRIBUTES { + nLength: u32, + lpSecurityDescriptor: *mut u8, + bInheritHandle: i32, +} + fn main() { // Set up logging tracing_subscriber::fmt() @@ -231,40 +279,43 @@ fn run_pipe_server() -> Result<()> { tracing::info!("Starting pipe server on {}", PIPE_NAME); loop { - // Create the pipe with security that allows all authenticated users - let pipe = unsafe { - // Create a security descriptor that allows everyone - let mut sd = SECURITY_DESCRIPTOR::default(); - InitializeSecurityDescriptor( - PSECURITY_DESCRIPTOR(&mut sd as *mut _ as *mut _), - SECURITY_DESCRIPTOR_REVISION, - )?; + // Create security descriptor that allows everyone + let mut sd = [0u8; 256]; + unsafe { + if InitializeSecurityDescriptor(sd.as_mut_ptr(), SECURITY_DESCRIPTOR_REVISION) == 0 { + tracing::error!("Failed to initialize security descriptor"); + std::thread::sleep(Duration::from_secs(1)); + continue; + } // Set NULL DACL = allow everyone - SetSecurityDescriptorDacl( - PSECURITY_DESCRIPTOR(&mut sd as *mut _ as *mut _), - true, - None, - false, - )?; + if SetSecurityDescriptorDacl(sd.as_mut_ptr(), 1, std::ptr::null_mut(), 0) == 0 { + tracing::error!("Failed to set security descriptor DACL"); + std::thread::sleep(Duration::from_secs(1)); + continue; + } + } - let mut sa = SECURITY_ATTRIBUTES { - nLength: std::mem::size_of::() as u32, - lpSecurityDescriptor: &mut sd as *mut _ as *mut _, - bInheritHandle: false.into(), - }; + let mut sa = SECURITY_ATTRIBUTES { + nLength: std::mem::size_of::() as u32, + lpSecurityDescriptor: sd.as_mut_ptr(), + bInheritHandle: 0, + }; - let pipe_name: Vec = PIPE_NAME.encode_utf16().chain(std::iter::once(0)).collect(); + // Create the pipe name as wide string + let pipe_name: Vec = PIPE_NAME.encode_utf16().chain(std::iter::once(0)).collect(); + // Create the named pipe + let pipe = unsafe { CreateNamedPipeW( - windows::core::PCWSTR(pipe_name.as_ptr()), + pipe_name.as_ptr(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 512, 512, 0, - Some(&mut sa), + &mut sa, ) }; @@ -277,13 +328,11 @@ fn run_pipe_server() -> Result<()> { tracing::info!("Waiting for client connection..."); // Wait for a client to connect - let connected = unsafe { ConnectNamedPipe(pipe, None) }; - if connected.is_err() { - // ERROR_PIPE_CONNECTED means client connected between CreateNamedPipe and ConnectNamedPipe - // That's OK, continue processing + let connected = unsafe { ConnectNamedPipe(pipe, std::ptr::null_mut()) }; + if connected == 0 { let err = std::io::Error::last_os_error(); + // ERROR_PIPE_CONNECTED (535) means client connected between Create and Connect if err.raw_os_error() != Some(535) { - // 535 = ERROR_PIPE_CONNECTED tracing::warn!("ConnectNamedPipe error: {}", err); } } @@ -297,13 +346,14 @@ fn run_pipe_server() -> Result<()> { let read_result = unsafe { ReadFile( pipe, - Some(&mut buffer), - Some(&mut bytes_read), - None, + buffer.as_mut_ptr(), + buffer.len() as u32, + &mut bytes_read, + std::ptr::null_mut(), ) }; - if read_result.is_ok() && bytes_read > 0 { + if read_result != 0 && bytes_read > 0 { let command = String::from_utf8_lossy(&buffer[..bytes_read as usize]); let command = command.trim(); @@ -334,15 +384,16 @@ fn run_pipe_server() -> Result<()> { // Write response let mut bytes_written = 0u32; - let _ = unsafe { + unsafe { WriteFile( pipe, - Some(response.as_bytes()), - Some(&mut bytes_written), - None, - ) - }; - let _ = unsafe { FlushFileBuffers(pipe) }; + response.as_ptr(), + response.len() as u32, + &mut bytes_written, + std::ptr::null_mut(), + ); + FlushFileBuffers(pipe); + } } // Disconnect and close the pipe