Uploading LaunchDaemon XPC service example

This commit is contained in:
Karmaz95
2024-12-20 02:02:18 +01:00
parent 83db8c656d
commit 684d03c491
4 changed files with 194 additions and 0 deletions

View File

@@ -0,0 +1,58 @@
# Compiler and flags
CC = gcc
CFLAGS = -Wall -Wextra -O2 # Enable warnings and basic optimization
FRAMEWORKS = -framework Foundation
# Binary names and paths
SERVICE_NAME = crimson_xpc_service
CLIENT_NAME = crimson_xpc_client
INSTALL_PATH = /usr/local/bin
LAUNCHD_PATH = /Library/LaunchDaemons
PLIST_NAME = com.crimson.xpc.message_service.plist
# Source files
SERVICE_SRC = crimson_xpc_service.c
CLIENT_SRC = crimson_xpc_client.c
# Default target builds both executables
all: $(SERVICE_NAME) $(CLIENT_NAME)
# Build the XPC service
$(SERVICE_NAME): $(SERVICE_SRC)
$(CC) $(CFLAGS) -o $@ $< $(FRAMEWORKS)
# Build the XPC client
$(CLIENT_NAME): $(CLIENT_SRC)
$(CC) $(CFLAGS) -o $@ $< $(FRAMEWORKS)
# Install everything (requires sudo)
install: all
@echo "Installing XPC service and client..."
sudo cp $(SERVICE_NAME) $(INSTALL_PATH)/
sudo cp $(PLIST_NAME) $(LAUNCHD_PATH)/
sudo launchctl unload $(LAUNCHD_PATH)/$(PLIST_NAME) 2>/dev/null || true
sudo launchctl load $(LAUNCHD_PATH)/$(PLIST_NAME)
@echo "Installation complete"
# Clean up compiled files
clean:
rm -f $(SERVICE_NAME) $(CLIENT_NAME)
# Uninstall everything (requires sudo)
uninstall:
@echo "Uninstalling XPC service and client..."
sudo launchctl unload $(LAUNCHD_PATH)/$(PLIST_NAME) 2>/dev/null || true
sudo rm -f $(INSTALL_PATH)/$(SERVICE_NAME)
sudo rm -f $(LAUNCHD_PATH)/$(PLIST_NAME)
@echo "Uninstallation complete"
# Help target to show available commands
help:
@echo "Available targets:"
@echo " make all - Build both service and client"
@echo " make install - Install and load the service (requires sudo)"
@echo " make clean - Remove compiled files"
@echo " make uninstall - Remove installed files (requires sudo)"
# Declare our phony targets (targets that don't create files)
.PHONY: all install clean uninstall help

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.crimson.xpc.message_service</string>
<key>Program</key>
<string>/usr/local/bin/crimson_xpc_service</string>
<key>MachServices</key>
<dict>
<key>com.crimson.xpc.message_service</key>
<true/>
</dict>
<key>KeepAlive</key>
<false/>
</dict>
</plist>

View File

@@ -0,0 +1,62 @@
// crimson_xpc_client.c
// This client demonstrates how to create and manage an XPC connection to communicate
// with a LaunchDaemon or LaunchAgent service
#include <xpc/xpc.h> // XPC framework for secure inter-process communication
#include <dispatch/dispatch.h> // GCD for asynchronous operations and event handling
#include <stdio.h> // Standard I/O for error reporting and status messages
#include <stdlib.h> // Standard library for program termination functions
int main(void) {
// Initialize an XPC connection to a Mach service
// The service name must match the label in the service's plist file
// XPC_CONNECTION_MACH_SERVICE_PRIVILEGED indicates this client expects
// to connect to a privileged service (typically a LaunchDaemon)
xpc_connection_t conn = xpc_connection_create_mach_service(
"com.crimson.xpc.message_service",
dispatch_get_main_queue(), // Main queue handles all connection events
XPC_CONNECTION_MACH_SERVICE_PRIVILEGED
);
// Configure an event handler for connection-level events
// This handler processes XPC_TYPE_ERROR events, which occur on
// connection failures, service termination, or invalid messages
xpc_connection_set_event_handler(conn, ^(xpc_object_t event) {
if (xpc_get_type(event) == XPC_TYPE_ERROR) {
fprintf(stderr, "Connection error: %s\n",
xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
}
});
// Activate the connection to begin processing events
// Must be called before any messages can be sent
xpc_connection_resume(conn);
// Create an XPC dictionary to encapsulate the message data
// XPC dictionaries are the primary container type for XPC messages
xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_string(message, "message_data", "Hello from Crimson!");
// Send message and handle response asynchronously
// The reply block executes when the service responds or on timeout
xpc_connection_send_message_with_reply(conn, message,
dispatch_get_main_queue(), ^(xpc_object_t reply) {
if (xpc_get_type(reply) == XPC_TYPE_DICTIONARY) {
printf("Reply status: %s\n",
xpc_dictionary_get_string(reply, "status"));
}
// Schedule program termination after reply processing
// Using dispatch_async ensures clean shutdown
dispatch_async(dispatch_get_main_queue(), ^{
exit(0);
});
});
// Decrement the message object's reference count
// XPC uses manual reference counting for memory management
xpc_release(message);
// Run the main dispatch loop to process asynchronous events
// This call never returns - program exits via the reply handler
dispatch_main();
}

View File

@@ -0,0 +1,57 @@
// crimson_xpc_service.c
// This implements an XPC service that can be launched as a LaunchDaemon or LaunchAgent
// It demonstrates basic XPC message handling and connection management patterns
#include <xpc/xpc.h> // XPC for inter-process communication
#include <dispatch/dispatch.h> // GCD for asynchronous event handling
#include <stdio.h> // Standard I/O for logging messages
int main(void) {
// Initialize the XPC service listener
// XPC_CONNECTION_MACH_SERVICE_LISTENER indicates this process will accept incoming connections
// The service name must match both the client and the service's launchd plist MachServices entry
xpc_connection_t service = xpc_connection_create_mach_service(
"com.crimson.xpc.message_service",
dispatch_get_main_queue(), // Main queue handles all service events
XPC_CONNECTION_MACH_SERVICE_LISTENER
);
// Set up handler for new client connections
// This outer handler processes connection establishment events
// Each new client connection creates a new peer object
xpc_connection_set_event_handler(service, ^(xpc_object_t peer) {
// Security check: Validate connection object type
// Early return prevents processing of invalid connection attempts
if (xpc_get_type(peer) != XPC_TYPE_CONNECTION) return;
// Configure message handler for this specific client connection
// Each client gets its own message handler to maintain separation
xpc_connection_set_event_handler(peer, ^(xpc_object_t message) {
// Only process dictionary-type messages for protocol compliance
if (xpc_get_type(message) == XPC_TYPE_DICTIONARY) {
// Extract message data with bounds checking via len parameter
// This prevents buffer overflow vulnerabilities
size_t len;
const void* data = xpc_dictionary_get_data(message, "message_data", &len);
if (data) printf("Received: %.*s\n", (int)len, (char*)data);
// Create and send reply to the client
// xpc_dictionary_create_reply maintains message context for proper routing
xpc_object_t reply = xpc_dictionary_create_reply(message);
xpc_dictionary_set_string(reply, "status", "received");
xpc_connection_send_message(peer, reply);
xpc_release(reply); // Clean up reply object to prevent memory leaks
}
});
// Activate this client's connection to begin processing its messages
xpc_connection_resume(peer);
});
// Activate the service listener to begin accepting connections
xpc_connection_resume(service);
// Run the main dispatch loop
// This service will continue running until terminated by launchd
dispatch_main();
}