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:
2025-12-28 20:55:36 -07:00
parent 68eab236bf
commit 598a6737de
2 changed files with 105 additions and 53 deletions

View File

@@ -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

View File

@@ -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,
}; };
let pipe_name: Vec<u16> = PIPE_NAME.encode_utf16().chain(std::iter::once(0)).collect(); // Create the pipe name as wide string
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