422 lines
17 KiB
C
422 lines
17 KiB
C
// Init service manager. The kernel bootstraps only init; init starts and supervises user-mode system services.
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <ntlite/exception.h>
|
|
#include <ntlite/process_loader.h>
|
|
#include <ntlite/resource.h>
|
|
#include <ntlite/security.h>
|
|
#include <ntlite/service_control.h>
|
|
#include <ntlite/syscall.h>
|
|
|
|
#define INIT_SERVICE_READY_TIMEOUT_MS 5000u
|
|
#define INIT_SERVICE_STOP_TIMEOUT_MS 5000u
|
|
|
|
typedef struct INIT_RESOURCE_SERVICE {
|
|
SYS_HANDLE Process;
|
|
SYS_HANDLE ControlEndpoint;
|
|
SYS_HANDLE AdminListener;
|
|
} INIT_RESOURCE_SERVICE;
|
|
|
|
// Writes one string through the privileged kernel debug channel.
|
|
static void debug_print(const char* text) {
|
|
SYS_PRINT_DEBUG_REQUEST request = { sizeof(request), SysSvFromCstr(text) };
|
|
(void)SysPrintDebug(&request);
|
|
}
|
|
|
|
// Writes one status value in hexadecimal without pulling in a formatting runtime.
|
|
static void debug_status(KSTATUS status) {
|
|
static const char digits[] = "0123456789ABCDEF";
|
|
char buffer[11] = "0x00000000";
|
|
for (uint32_t i = 0; i < 8; ++i)
|
|
buffer[2 + i] = digits[(status >> ((7u - i) * 4u)) & 0xFu];
|
|
debug_print(buffer);
|
|
}
|
|
|
|
// Exits init without relying on a compiler runtime return path.
|
|
static void exit_now(int code) {
|
|
for (;;) SysExitProcess(code);
|
|
}
|
|
|
|
// Derives the least-privileged security context required by RSSRV.
|
|
static KSTATUS derive_resource_service_context(SYS_HANDLE* contextOut) {
|
|
if (!contextOut) return NTLSTATUS_INVALID_PARAMETER;
|
|
*contextOut = SYS_HANDLE_INVALID;
|
|
SYS_DERIVE_SECURITY_CONTEXT_REQUEST request;
|
|
memset(&request, 0, sizeof(request));
|
|
request.Size = sizeof(request);
|
|
request.SourceContext = SYS_SECURITY_CONTEXT_CURRENT;
|
|
request.EffectivePrivileges = SYS_PRIV_SYSTEM_SERVICE_REGISTER;
|
|
request.DelegablePrivileges = 0;
|
|
request.ServiceClaims = SYS_SERVICE_CLAIM_RESOURCE;
|
|
request.DelegableServiceClaims = 0;
|
|
request.Flags = SYS_SECURITY_CONTEXT_FLAG_NONE;
|
|
request.ContextHandleOut = contextOut;
|
|
return SysDeriveSecurityContext(&request);
|
|
}
|
|
|
|
// Creates one private Port listener used only between init and a managed service.
|
|
static KSTATUS create_control_listener(SYS_HANDLE* listenerOut) {
|
|
SYS_CREATE_PORT_REQUEST request;
|
|
memset(&request, 0, sizeof(request));
|
|
request.Size = sizeof(request);
|
|
request.Flags = SYS_PORT_FLAG_NONE;
|
|
request.Backlog = 1;
|
|
request.HandleOut = listenerOut;
|
|
return SysCreatePort(&request);
|
|
}
|
|
|
|
// Connects init to the private control listener delegated to the child.
|
|
static KSTATUS connect_control_listener(SYS_HANDLE listener, SYS_HANDLE* endpointOut) {
|
|
SYS_CONNECT_PORT_REQUEST request;
|
|
memset(&request, 0, sizeof(request));
|
|
request.Size = sizeof(request);
|
|
request.ListenerHandle = listener;
|
|
request.Flags = SYS_PORT_CONNECT_FLAG_NONE;
|
|
request.EndpointHandleOut = endpointOut;
|
|
return SysConnectPort(&request);
|
|
}
|
|
|
|
// Waits for RSSRV readiness and receives its private administrative namespace listener.
|
|
static KSTATUS wait_resource_service_ready(SYS_HANDLE controlEndpoint, SYS_HANDLE* adminListenerOut) {
|
|
if (!adminListenerOut) return NTLSTATUS_INVALID_PARAMETER;
|
|
*adminListenerOut = SYS_HANDLE_INVALID;
|
|
|
|
NTL_SERVICE_CONTROL_MESSAGE message;
|
|
SYS_PORT_MESSAGE_INFO info;
|
|
SYS_HANDLE adminListener = SYS_HANDLE_INVALID;
|
|
memset(&message, 0, sizeof(message));
|
|
memset(&info, 0, sizeof(info));
|
|
info.Size = sizeof(info);
|
|
|
|
SYS_RECEIVE_PORT_REQUEST receive;
|
|
memset(&receive, 0, sizeof(receive));
|
|
receive.Size = sizeof(receive);
|
|
receive.EndpointHandle = controlEndpoint;
|
|
receive.Flags = SYS_PORT_FLAG_NONE;
|
|
receive.Payload = &message;
|
|
receive.PayloadCapacity = sizeof(message);
|
|
receive.HandlesOut = &adminListener;
|
|
receive.HandleCapacity = 1;
|
|
receive.InfoOut = &info;
|
|
receive.TimeoutMilliseconds = INIT_SERVICE_READY_TIMEOUT_MS;
|
|
KSTATUS status = SysReceivePort(&receive);
|
|
if (!KSUCCESS(status)) return status;
|
|
if (info.Type != SYS_PORT_MESSAGE_NOTIFICATION || info.PayloadLength != sizeof(message) ||
|
|
info.HandleCount != 1 || adminListener == SYS_HANDLE_INVALID ||
|
|
message.Size != sizeof(message) || message.Version != NTL_SERVICE_CONTROL_VERSION ||
|
|
message.Operation != NTL_SERVICE_CONTROL_READY ||
|
|
message.Flags != NTL_SERVICE_READY_FLAG_RESOURCE_ADMIN_LISTENER ||
|
|
!KSUCCESS(message.Status)) {
|
|
if (adminListener != SYS_HANDLE_INVALID) (void)SysCloseHandle(adminListener);
|
|
return NTLSTATUS_FAILURE;
|
|
}
|
|
|
|
*adminListenerOut = adminListener;
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Verifies private namespace administration and public /SystemRoot lookup before declaring RSSRV usable.
|
|
static KSTATUS verify_resource_service(SYS_HANDLE adminListener, SYS_HANDLE namespaceVolume) {
|
|
SYS_HANDLE admin = SYS_HANDLE_INVALID;
|
|
KSTATUS status = NtlResourceConnectListener(adminListener, &admin);
|
|
if (!KSUCCESS(status)) return status;
|
|
|
|
uint32_t kind = NTL_RESOURCE_KIND_INVALID;
|
|
status = NtlResourceQuery(admin, SYS_SV_LITERAL("/SystemRoot"), &kind);
|
|
if (!KSUCCESS(status) || kind != NTL_RESOURCE_KIND_VOLUME) {
|
|
(void)NtlResourceClose(admin);
|
|
return KSUCCESS(status) ? NTLSTATUS_FAILURE : status;
|
|
}
|
|
|
|
// Exercise capability-based administration without leaving a bootstrap-only namespace entry behind.
|
|
status = NtlResourceBind(admin, SYS_SV_LITERAL("/Init/BootstrapProbe"),
|
|
NTL_RESOURCE_KIND_VOLUME, namespaceVolume,
|
|
SYS_RIGHT_TRANSFER | SYS_RIGHT_QUERY | SYS_VOLUME_RIGHT_OPEN,
|
|
SYS_RIGHT_QUERY | SYS_VOLUME_RIGHT_OPEN);
|
|
if (!KSUCCESS(status)) {
|
|
(void)NtlResourceClose(admin);
|
|
return status;
|
|
}
|
|
|
|
SYS_HANDLE publicSession = SYS_HANDLE_INVALID;
|
|
status = NtlResourceConnect(&publicSession);
|
|
if (!KSUCCESS(status)) {
|
|
(void)NtlResourceUnbind(admin, SYS_SV_LITERAL("/Init/BootstrapProbe"));
|
|
(void)NtlResourceClose(admin);
|
|
return status;
|
|
}
|
|
|
|
SYS_HANDLE volume = SYS_HANDLE_INVALID;
|
|
status = NtlResourceLookup(publicSession, SYS_SV_LITERAL("/SystemRoot"),
|
|
SYS_RIGHT_QUERY | SYS_VOLUME_RIGHT_OPEN, &volume);
|
|
if (KSUCCESS(status)) (void)SysCloseHandle(volume);
|
|
if (!KSUCCESS(status)) {
|
|
(void)NtlResourceClose(publicSession);
|
|
(void)NtlResourceUnbind(admin, SYS_SV_LITERAL("/Init/BootstrapProbe"));
|
|
(void)NtlResourceClose(admin);
|
|
return status;
|
|
}
|
|
|
|
kind = NTL_RESOURCE_KIND_INVALID;
|
|
status = NtlResourceQuery(publicSession, SYS_SV_LITERAL("/Init/BootstrapProbe"), &kind);
|
|
if (!KSUCCESS(status) || kind != NTL_RESOURCE_KIND_VOLUME) {
|
|
(void)NtlResourceClose(publicSession);
|
|
(void)NtlResourceUnbind(admin, SYS_SV_LITERAL("/Init/BootstrapProbe"));
|
|
(void)NtlResourceClose(admin);
|
|
return KSUCCESS(status) ? NTLSTATUS_FAILURE : status;
|
|
}
|
|
|
|
status = NtlResourceUnbind(admin, SYS_SV_LITERAL("/Init/BootstrapProbe"));
|
|
if (KSUCCESS(status)) {
|
|
kind = NTL_RESOURCE_KIND_INVALID;
|
|
KSTATUS queryStatus = NtlResourceQuery(publicSession, SYS_SV_LITERAL("/Init/BootstrapProbe"), &kind);
|
|
if (queryStatus != NTLSTATUS_NOT_FOUND) status = NTLSTATUS_FAILURE;
|
|
}
|
|
|
|
(void)NtlResourceClose(publicSession);
|
|
(void)NtlResourceClose(admin);
|
|
return status;
|
|
}
|
|
|
|
// Tears down retained parent-side service handles without sending a stop message.
|
|
static void close_resource_service(INIT_RESOURCE_SERVICE* service) {
|
|
if (!service) return;
|
|
if (service->ControlEndpoint != SYS_HANDLE_INVALID) {
|
|
(void)SysDisconnectPort(service->ControlEndpoint);
|
|
(void)SysCloseHandle(service->ControlEndpoint);
|
|
}
|
|
if (service->AdminListener != SYS_HANDLE_INVALID)
|
|
(void)SysCloseHandle(service->AdminListener);
|
|
if (service->Process != SYS_HANDLE_INVALID)
|
|
(void)SysCloseHandle(service->Process);
|
|
service->Process = SYS_HANDLE_INVALID;
|
|
service->ControlEndpoint = SYS_HANDLE_INVALID;
|
|
service->AdminListener = SYS_HANDLE_INVALID;
|
|
}
|
|
|
|
// Starts one restricted, import-free RSSRV instance through the userspace process loader.
|
|
static KSTATUS start_resource_service(SYS_HANDLE systemVolume,
|
|
SYS_HANDLE namespaceVolume,
|
|
SYS_HANDLE volumeManager,
|
|
SYS_HANDLE resourceContext,
|
|
INIT_RESOURCE_SERVICE* serviceOut) {
|
|
if (!serviceOut) return NTLSTATUS_INVALID_PARAMETER;
|
|
memset(serviceOut, 0, sizeof(*serviceOut));
|
|
|
|
SYS_HANDLE controlListener = SYS_HANDLE_INVALID;
|
|
KSTATUS status = create_control_listener(&controlListener);
|
|
if (!KSUCCESS(status)) return status;
|
|
|
|
NTL_PROCESS_STARTUP_CAPABILITY startup[3];
|
|
memset(startup, 0, sizeof(startup));
|
|
startup[0].Tag = SYS_STARTUP_CAP_SYSTEM_VOLUME_NAMESPACE;
|
|
startup[0].SourceHandle = namespaceVolume;
|
|
startup[0].DesiredRights = SYS_RIGHT_QUERY | SYS_RIGHT_TRANSFER |
|
|
SYS_VOLUME_RIGHT_OPEN | SYS_VOLUME_RIGHT_MODIFY;
|
|
startup[0].DuplicateFlags = SYS_DUPLICATE_HANDLE_FLAG_NONE;
|
|
startup[1].Tag = SYS_STARTUP_CAP_SERVICE_CONTROL;
|
|
startup[1].SourceHandle = controlListener;
|
|
startup[1].DesiredRights = SYS_RIGHT_QUERY | SYS_PORT_RIGHT_ACCEPT;
|
|
startup[1].DuplicateFlags = SYS_DUPLICATE_HANDLE_FLAG_NONE;
|
|
startup[2].Tag = SYS_STARTUP_CAP_VOLUME_MANAGER;
|
|
startup[2].SourceHandle = volumeManager;
|
|
startup[2].DesiredRights = SYS_RIGHT_QUERY |
|
|
SYS_VOLUME_MANAGER_RIGHT_ENUMERATE | SYS_VOLUME_MANAGER_RIGHT_OPEN |
|
|
SYS_VOLUME_MANAGER_RIGHT_WAIT;
|
|
startup[2].DuplicateFlags = SYS_DUPLICATE_HANDLE_FLAG_NONE;
|
|
|
|
SYS_HANDLE process = SYS_HANDLE_INVALID;
|
|
NTL_CREATE_PROCESS_AT_REQUEST create;
|
|
memset(&create, 0, sizeof(create));
|
|
create.Size = sizeof(create);
|
|
create.Flags = NTL_PROCESS_LOAD_FLAG_NONE;
|
|
create.RootHandle = systemVolume;
|
|
create.Path = SYS_SV_LITERAL("system/rssrv.exe");
|
|
create.CommandLine = SYS_SV_LITERAL("system/rssrv.exe");
|
|
create.SecurityContext = resourceContext;
|
|
create.ModuleRootHandle = systemVolume;
|
|
create.ModuleDirectory = SYS_SV_LITERAL("system");
|
|
create.ResolveModuleName = NtlResolveModuleNameIdentity;
|
|
create.ResolveContext = (void*)0;
|
|
create.StartupCapabilities = startup;
|
|
create.StartupCapabilityCount = 3;
|
|
create.ProcessHandleOut = &process;
|
|
status = NtlCreateProcessAt(&create);
|
|
if (!KSUCCESS(status)) {
|
|
(void)SysCloseHandle(controlListener);
|
|
return status;
|
|
}
|
|
|
|
SYS_HANDLE controlEndpoint = SYS_HANDLE_INVALID;
|
|
status = connect_control_listener(controlListener, &controlEndpoint);
|
|
(void)SysCloseHandle(controlListener);
|
|
if (!KSUCCESS(status)) {
|
|
(void)SysKillProcess(process, 1);
|
|
(void)SysCloseHandle(process);
|
|
return status;
|
|
}
|
|
|
|
SYS_HANDLE adminListener = SYS_HANDLE_INVALID;
|
|
status = wait_resource_service_ready(controlEndpoint, &adminListener);
|
|
if (KSUCCESS(status))
|
|
status = verify_resource_service(adminListener, namespaceVolume);
|
|
if (!KSUCCESS(status)) {
|
|
if (adminListener != SYS_HANDLE_INVALID) (void)SysCloseHandle(adminListener);
|
|
(void)SysDisconnectPort(controlEndpoint);
|
|
(void)SysCloseHandle(controlEndpoint);
|
|
(void)SysKillProcess(process, 1);
|
|
(void)SysCloseHandle(process);
|
|
return status;
|
|
}
|
|
|
|
serviceOut->Process = process;
|
|
serviceOut->ControlEndpoint = controlEndpoint;
|
|
serviceOut->AdminListener = adminListener;
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Requests graceful RSSRV shutdown, falling back to process termination if it stops responding.
|
|
static void stop_resource_service(INIT_RESOURCE_SERVICE* service) {
|
|
if (!service || service->Process == SYS_HANDLE_INVALID) return;
|
|
|
|
NTL_SERVICE_CONTROL_MESSAGE message;
|
|
memset(&message, 0, sizeof(message));
|
|
message.Size = sizeof(message);
|
|
message.Version = NTL_SERVICE_CONTROL_VERSION;
|
|
message.Operation = NTL_SERVICE_CONTROL_STOP;
|
|
|
|
SYS_SEND_PORT_REQUEST send;
|
|
memset(&send, 0, sizeof(send));
|
|
send.Size = sizeof(send);
|
|
send.EndpointHandle = service->ControlEndpoint;
|
|
send.Flags = SYS_PORT_FLAG_NONE;
|
|
send.Payload = &message;
|
|
send.PayloadLength = sizeof(message);
|
|
send.TimeoutMilliseconds = INIT_SERVICE_STOP_TIMEOUT_MS;
|
|
(void)SysSendPort(&send);
|
|
|
|
KSTATUS status = SysWaitSingle(service->Process, SYS_WAIT_FLAG_NONE, INIT_SERVICE_STOP_TIMEOUT_MS);
|
|
if (!KSUCCESS(status)) {
|
|
(void)SysKillProcess(service->Process, 1);
|
|
(void)SysWait(service->Process, SYS_WAIT_FLAG_NONE);
|
|
}
|
|
close_resource_service(service);
|
|
}
|
|
|
|
// Starts the configured native session child directly beneath the system-volume capability.
|
|
static KSTATUS start_configured_child(SYS_HANDLE systemVolume, SYS_STRING_VIEW path, SYS_HANDLE* processOut) {
|
|
NTL_CREATE_PROCESS_AT_REQUEST create;
|
|
memset(&create, 0, sizeof(create));
|
|
create.Size = sizeof(create);
|
|
create.Flags = NTL_PROCESS_LOAD_FLAG_NONE;
|
|
create.RootHandle = systemVolume;
|
|
create.Path = path;
|
|
create.CommandLine = path;
|
|
create.SecurityContext = SYS_SECURITY_CONTEXT_CURRENT;
|
|
create.ModuleRootHandle = systemVolume;
|
|
create.ModuleDirectory = SYS_SV_LITERAL("system");
|
|
create.ResolveModuleName = NtlResolveSystemModuleName;
|
|
create.ProcessHandleOut = processOut;
|
|
return NtlCreateProcessAt(&create);
|
|
}
|
|
|
|
// Initializes init state and supervises RSSRV plus the configured session process.
|
|
void _start(void) {
|
|
KSTATUS status = NtlInitializeExceptionRuntime();
|
|
if (!KSUCCESS(status)) exit_now((int)status);
|
|
|
|
SYS_HANDLE systemVolume = SysGetStartupCapability(SYS_STARTUP_CAP_SYSTEM_VOLUME);
|
|
SYS_HANDLE namespaceVolume = SysGetStartupCapability(SYS_STARTUP_CAP_SYSTEM_VOLUME_NAMESPACE);
|
|
SYS_HANDLE volumeManager = SysGetStartupCapability(SYS_STARTUP_CAP_VOLUME_MANAGER);
|
|
if (systemVolume == SYS_HANDLE_INVALID || namespaceVolume == SYS_HANDLE_INVALID ||
|
|
volumeManager == SYS_HANDLE_INVALID) {
|
|
debug_print("[init] FATAL: bootstrap storage capability missing\n");
|
|
exit_now(1);
|
|
}
|
|
|
|
SYS_HANDLE resourceContext = SYS_HANDLE_INVALID;
|
|
status = derive_resource_service_context(&resourceContext);
|
|
if (!KSUCCESS(status)) {
|
|
debug_print("[init] FATAL: cannot derive RSSRV security context: ");
|
|
debug_status(status);
|
|
debug_print("\n");
|
|
exit_now(1);
|
|
}
|
|
|
|
SYS_STRING_VIEW childPath = SysGetPeb()->CommandLine;
|
|
if (!childPath.Data || childPath.Length == 0) {
|
|
debug_print("[init] FATAL: no configured session process\n");
|
|
exit_now(1);
|
|
}
|
|
|
|
for (;;) {
|
|
INIT_RESOURCE_SERVICE resourceService;
|
|
status = start_resource_service(systemVolume, namespaceVolume, volumeManager, resourceContext, &resourceService);
|
|
if (!KSUCCESS(status)) {
|
|
debug_print("[init] FATAL: RSSRV startup failed: ");
|
|
debug_status(status);
|
|
debug_print("\n");
|
|
exit_now(1);
|
|
}
|
|
debug_print("[init] RSSRV ready\n");
|
|
|
|
for (;;) {
|
|
SYS_HANDLE child = SYS_HANDLE_INVALID;
|
|
status = start_configured_child(systemVolume, childPath, &child);
|
|
if (!KSUCCESS(status)) {
|
|
debug_print("[init] FATAL: failed to start configured child: ");
|
|
debug_status(status);
|
|
debug_print("\n");
|
|
stop_resource_service(&resourceService);
|
|
exit_now(1);
|
|
}
|
|
debug_print("[init] session process started\n");
|
|
|
|
SYS_HANDLE waits[2] = { resourceService.Process, child };
|
|
uint32_t signaled = 0;
|
|
SYS_WAIT_MANY_REQUEST wait;
|
|
memset(&wait, 0, sizeof(wait));
|
|
wait.Size = sizeof(wait);
|
|
wait.Flags = SYS_WAIT_FLAG_NONE;
|
|
wait.Handles = waits;
|
|
wait.Count = 2;
|
|
wait.SignaledIndexOut = &signaled;
|
|
wait.TimeoutMilliseconds = SYS_WAIT_TIMEOUT_INFINITE;
|
|
status = SysWaitMany(&wait);
|
|
if (!KSUCCESS(status)) {
|
|
(void)SysKillProcess(child, 1);
|
|
(void)SysCloseHandle(child);
|
|
stop_resource_service(&resourceService);
|
|
debug_print("[init] FATAL: service wait failed\n");
|
|
exit_now(1);
|
|
}
|
|
|
|
if (signaled == 0) {
|
|
int32_t serviceExit = -1;
|
|
(void)SysGetProcessExitCode(resourceService.Process, &serviceExit);
|
|
debug_print("[init] RSSRV exited unexpectedly; restarting service plane\n");
|
|
(void)SysKillProcess(child, 1);
|
|
(void)SysWait(child, SYS_WAIT_FLAG_NONE);
|
|
(void)SysCloseHandle(child);
|
|
close_resource_service(&resourceService);
|
|
break;
|
|
}
|
|
|
|
int32_t exitCode = -1;
|
|
status = SysGetProcessExitCode(child, &exitCode);
|
|
(void)SysCloseHandle(child);
|
|
if (!KSUCCESS(status)) exitCode = 1;
|
|
if (exitCode == 0) {
|
|
stop_resource_service(&resourceService);
|
|
debug_print("[init] done\n");
|
|
(void)SysCloseHandle(resourceContext);
|
|
exit_now(0);
|
|
}
|
|
debug_print("[init] session process crashed; restarting\n");
|
|
}
|
|
}
|
|
}
|