Fix SAS Service build errors
- Use raw FFI for named pipe operations instead of windows crate APIs - Add Win32_System_IO feature to Cargo.toml - Define pipe constants manually to avoid missing exports 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -77,6 +77,7 @@ windows = { version = "0.58", features = [
|
|||||||
"Win32_Storage_FileSystem",
|
"Win32_Storage_FileSystem",
|
||||||
"Win32_System_Pipes",
|
"Win32_System_Pipes",
|
||||||
"Win32_System_SystemServices",
|
"Win32_System_SystemServices",
|
||||||
|
"Win32_System_IO",
|
||||||
]}
|
]}
|
||||||
|
|
||||||
# Windows service support
|
# Windows service support
|
||||||
|
|||||||
@@ -4,26 +4,13 @@
|
|||||||
//! The agent communicates with this service via named pipe IPC.
|
//! The agent communicates with this service via named pipe IPC.
|
||||||
|
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write as IoWrite};
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use windows::core::{s, w, PCSTR};
|
use windows::core::{s, w};
|
||||||
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::Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryW};
|
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::{
|
use windows_service::{
|
||||||
define_windows_service,
|
define_windows_service,
|
||||||
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 PIPE_NAME: &str = r"\\.\pipe\guruconnect-sas";
|
||||||
const INSTALL_DIR: &str = r"C:\Program Files\GuruConnect";
|
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() {
|
fn main() {
|
||||||
// Set up logging
|
// Set up logging
|
||||||
tracing_subscriber::fmt()
|
tracing_subscriber::fmt()
|
||||||
@@ -231,40 +279,43 @@ fn run_pipe_server() -> Result<()> {
|
|||||||
tracing::info!("Starting pipe server on {}", PIPE_NAME);
|
tracing::info!("Starting pipe server on {}", PIPE_NAME);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Create the pipe with security that allows all authenticated users
|
// Create security descriptor that allows everyone
|
||||||
let pipe = unsafe {
|
let mut sd = [0u8; 256];
|
||||||
// Create a security descriptor that allows everyone
|
unsafe {
|
||||||
let mut sd = SECURITY_DESCRIPTOR::default();
|
if InitializeSecurityDescriptor(sd.as_mut_ptr(), SECURITY_DESCRIPTOR_REVISION) == 0 {
|
||||||
InitializeSecurityDescriptor(
|
tracing::error!("Failed to initialize security descriptor");
|
||||||
PSECURITY_DESCRIPTOR(&mut sd as *mut _ as *mut _),
|
std::thread::sleep(Duration::from_secs(1));
|
||||||
SECURITY_DESCRIPTOR_REVISION,
|
continue;
|
||||||
)?;
|
}
|
||||||
|
|
||||||
// Set NULL DACL = allow everyone
|
// Set NULL DACL = allow everyone
|
||||||
SetSecurityDescriptorDacl(
|
if SetSecurityDescriptorDacl(sd.as_mut_ptr(), 1, std::ptr::null_mut(), 0) == 0 {
|
||||||
PSECURITY_DESCRIPTOR(&mut sd as *mut _ as *mut _),
|
tracing::error!("Failed to set security descriptor DACL");
|
||||||
true,
|
std::thread::sleep(Duration::from_secs(1));
|
||||||
None,
|
continue;
|
||||||
false,
|
}
|
||||||
)?;
|
}
|
||||||
|
|
||||||
let mut sa = SECURITY_ATTRIBUTES {
|
let mut sa = SECURITY_ATTRIBUTES {
|
||||||
nLength: std::mem::size_of::<SECURITY_ATTRIBUTES>() as u32,
|
nLength: std::mem::size_of::<SECURITY_ATTRIBUTES>() as u32,
|
||||||
lpSecurityDescriptor: &mut sd as *mut _ as *mut _,
|
lpSecurityDescriptor: sd.as_mut_ptr(),
|
||||||
bInheritHandle: false.into(),
|
bInheritHandle: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Create the pipe name as wide string
|
||||||
let pipe_name: Vec<u16> = PIPE_NAME.encode_utf16().chain(std::iter::once(0)).collect();
|
let pipe_name: Vec<u16> = PIPE_NAME.encode_utf16().chain(std::iter::once(0)).collect();
|
||||||
|
|
||||||
|
// Create the named pipe
|
||||||
|
let pipe = unsafe {
|
||||||
CreateNamedPipeW(
|
CreateNamedPipeW(
|
||||||
windows::core::PCWSTR(pipe_name.as_ptr()),
|
pipe_name.as_ptr(),
|
||||||
PIPE_ACCESS_DUPLEX,
|
PIPE_ACCESS_DUPLEX,
|
||||||
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
|
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
|
||||||
PIPE_UNLIMITED_INSTANCES,
|
PIPE_UNLIMITED_INSTANCES,
|
||||||
512,
|
512,
|
||||||
512,
|
512,
|
||||||
0,
|
0,
|
||||||
Some(&mut sa),
|
&mut sa,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -277,13 +328,11 @@ fn run_pipe_server() -> Result<()> {
|
|||||||
tracing::info!("Waiting for client connection...");
|
tracing::info!("Waiting for client connection...");
|
||||||
|
|
||||||
// Wait for a client to connect
|
// Wait for a client to connect
|
||||||
let connected = unsafe { ConnectNamedPipe(pipe, None) };
|
let connected = unsafe { ConnectNamedPipe(pipe, std::ptr::null_mut()) };
|
||||||
if connected.is_err() {
|
if connected == 0 {
|
||||||
// ERROR_PIPE_CONNECTED means client connected between CreateNamedPipe and ConnectNamedPipe
|
|
||||||
// That's OK, continue processing
|
|
||||||
let err = std::io::Error::last_os_error();
|
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) {
|
if err.raw_os_error() != Some(535) {
|
||||||
// 535 = ERROR_PIPE_CONNECTED
|
|
||||||
tracing::warn!("ConnectNamedPipe error: {}", err);
|
tracing::warn!("ConnectNamedPipe error: {}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -297,13 +346,14 @@ fn run_pipe_server() -> Result<()> {
|
|||||||
let read_result = unsafe {
|
let read_result = unsafe {
|
||||||
ReadFile(
|
ReadFile(
|
||||||
pipe,
|
pipe,
|
||||||
Some(&mut buffer),
|
buffer.as_mut_ptr(),
|
||||||
Some(&mut bytes_read),
|
buffer.len() as u32,
|
||||||
None,
|
&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 = String::from_utf8_lossy(&buffer[..bytes_read as usize]);
|
||||||
let command = command.trim();
|
let command = command.trim();
|
||||||
|
|
||||||
@@ -334,15 +384,16 @@ fn run_pipe_server() -> Result<()> {
|
|||||||
|
|
||||||
// Write response
|
// Write response
|
||||||
let mut bytes_written = 0u32;
|
let mut bytes_written = 0u32;
|
||||||
let _ = unsafe {
|
unsafe {
|
||||||
WriteFile(
|
WriteFile(
|
||||||
pipe,
|
pipe,
|
||||||
Some(response.as_bytes()),
|
response.as_ptr(),
|
||||||
Some(&mut bytes_written),
|
response.len() as u32,
|
||||||
None,
|
&mut bytes_written,
|
||||||
)
|
std::ptr::null_mut(),
|
||||||
};
|
);
|
||||||
let _ = unsafe { FlushFileBuffers(pipe) };
|
FlushFileBuffers(pipe);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disconnect and close the pipe
|
// Disconnect and close the pipe
|
||||||
|
|||||||
Reference in New Issue
Block a user