redo boot around an explicit system volume and fixed kernel/HAL images, redo volumes and filesystem paths around capabilities instead of drive numbers and kernel CWD state, add a capability/security model with derived and revocable handles and security contexts, add port IPC and rssrv to manage the userspace resource namespace and publish the system volume, move process, dll and path loading to userspace and add process builder syscalls, init now supervises rssrv and the session process, redo the syscall ABI around request structs and four arguments, add a volume manager and proper partition arrival/removal handling so FAT and NTFS mount dynamically, clean up the HAL/DDK boundary so drivers don't depend on HAL internals, save fpu state properly now, the serial driver doesn't hang on a missing port, fix AHCI startup races, fix timeout accounting, fix shared mmap writeback, more smoke tests
This commit is contained in:
1 parent
4add6789ba
commit
f882d339fb
232 files changed
+20934
-8898
No files matched your search
@@ -0,0 +1,319 @@
|
||||
// Security-context smoke test. This validates authority attenuation, assignment, and ambient privilege enforcement.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/syscall.h>
|
||||
#include <ntlite/path.h>
|
||||
#include <ntlite/process_loader.h>
|
||||
|
||||
// Writes one string from the privileged parent process.
|
||||
static void print(const char* text) {
|
||||
SYS_WRITE_CONSOLE_REQUEST request = { sizeof(request), SysSvFromCstr(text) };
|
||||
(void)SysWriteConsole(&request);
|
||||
}
|
||||
|
||||
// Writes one unsigned integer from the privileged parent process.
|
||||
static void print_u32(uint32_t value) {
|
||||
char digits[16];
|
||||
uint32_t count = 0;
|
||||
if (value == 0) {
|
||||
print("0");
|
||||
return;
|
||||
}
|
||||
while (value && count < sizeof(digits)) {
|
||||
digits[count++] = (char)('0' + value % 10u);
|
||||
value /= 10u;
|
||||
}
|
||||
while (count) {
|
||||
char one[2] = { digits[--count], 0 };
|
||||
print(one);
|
||||
}
|
||||
}
|
||||
|
||||
// Exits without relying on a C runtime return path.
|
||||
static void exit_now(int32_t code) {
|
||||
while (true) (void)SysExitProcess(code);
|
||||
}
|
||||
|
||||
// Fails the privileged parent with a visible diagnostic.
|
||||
static void fail_status(const char* operation, KSTATUS status) {
|
||||
print("security_smoke: ");
|
||||
print(operation);
|
||||
print(" status=");
|
||||
print_u32(status);
|
||||
print("\n");
|
||||
exit_now(1);
|
||||
}
|
||||
|
||||
// Parses one unsigned decimal value without requiring formatted libc helpers.
|
||||
static bool parse_u32(SYS_STRING_VIEW text, size_t start, uint32_t* valueOut) {
|
||||
if (!valueOut || start >= text.Length) return false;
|
||||
uint32_t value = 0;
|
||||
bool any = false;
|
||||
for (size_t i = start; i < text.Length; i++) {
|
||||
char c = text.Data[i];
|
||||
if (c < '0' || c > '9') return false;
|
||||
uint32_t digit = (uint32_t)(c - '0');
|
||||
if (value > (UINT32_MAX - digit) / 10u) return false;
|
||||
value = value * 10u + digit;
|
||||
any = true;
|
||||
}
|
||||
if (!any) return false;
|
||||
*valueOut = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Appends one unsigned decimal value to a bounded command string.
|
||||
static bool append_u32(char* buffer, size_t capacity, size_t* lengthInOut, uint32_t value) {
|
||||
if (!buffer || !lengthInOut || *lengthInOut >= capacity) return false;
|
||||
char reverse[16];
|
||||
size_t count = 0;
|
||||
do {
|
||||
reverse[count++] = (char)('0' + value % 10u);
|
||||
value /= 10u;
|
||||
} while (value && count < sizeof(reverse));
|
||||
if (*lengthInOut + count + 1 > capacity) return false;
|
||||
while (count) buffer[(*lengthInOut)++] = reverse[--count];
|
||||
buffer[*lengthInOut] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
// Runs inside the stripped child and returns a distinct failure code for each leaked ambient authority.
|
||||
static int run_restricted_child(uint32_t parentPid) {
|
||||
SYS_SECURITY_CONTEXT_INFO info;
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.Size = sizeof(info);
|
||||
KSTATUS status = SysQuerySecurityContext(SYS_SECURITY_CONTEXT_CURRENT, &info);
|
||||
if (!KSUCCESS(status)) return 10;
|
||||
if (info.EffectivePrivileges || info.DelegablePrivileges ||
|
||||
info.ServiceClaims || info.DelegableServiceClaims)
|
||||
return 11;
|
||||
|
||||
SYS_HANDLE process = SYS_HANDLE_INVALID;
|
||||
status = SysOpenProcess(parentPid, SYS_RIGHT_QUERY, &process);
|
||||
if (status != NTLSTATUS_ACCESS_DENIED) return 12;
|
||||
|
||||
size_t processCount = 0;
|
||||
status = SysGetProcessList((SYS_PROCESS_INFO*)0, 0, &processCount);
|
||||
if (status != NTLSTATUS_ACCESS_DENIED) return 13;
|
||||
|
||||
size_t required = 0;
|
||||
status = SysQueryKernelStats(SYS_KERNEL_STATS_SCHEDULER, (void*)0, 0, &required);
|
||||
if (status != NTLSTATUS_ACCESS_DENIED) return 14;
|
||||
|
||||
SYS_HANDLE inputQueue = SYS_HANDLE_INVALID;
|
||||
status = SysOpenInputQueue(0, &inputQueue);
|
||||
if (status != NTLSTATUS_ACCESS_DENIED) return 15;
|
||||
|
||||
SYS_DISPLAY_INFO displayInfo;
|
||||
memset(&displayInfo, 0, sizeof(displayInfo));
|
||||
status = SysQueryDisplayInfo(&displayInfo);
|
||||
if (status != NTLSTATUS_ACCESS_DENIED) return 16;
|
||||
|
||||
SYS_HANDLE socketHandle = SYS_HANDLE_INVALID;
|
||||
status = SysCreateSocket(0, 0, 0, &socketHandle);
|
||||
if (status != NTLSTATUS_ACCESS_DENIED) return 17;
|
||||
|
||||
SYS_PRINT_DEBUG_REQUEST debugRequest = { sizeof(debugRequest), SysSvFromParts((const char*)0, 0) };
|
||||
status = SysPrintDebug(&debugRequest);
|
||||
if (status != NTLSTATUS_ACCESS_DENIED) return 18;
|
||||
|
||||
SYS_WRITE_CONSOLE_REQUEST consoleRequest = { sizeof(consoleRequest), SysSvFromParts((const char*)0, 0) };
|
||||
status = SysWriteConsole(&consoleRequest);
|
||||
if (status != NTLSTATUS_ACCESS_DENIED) return 19;
|
||||
|
||||
SYS_PEB* peb = SysGetPeb();
|
||||
if (!peb || !peb->ImagePath.Data || peb->ImagePath.Length == 0) return 20;
|
||||
SYS_HANDLE file = SYS_HANDLE_INVALID;
|
||||
status = NtlOpenFile(peb->ImagePath,
|
||||
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
|
||||
SYS_FILE_OPEN_EXISTING,
|
||||
SYS_FILE_FLAG_NONE,
|
||||
&file);
|
||||
if (!KSUCCESS(status)) return 21;
|
||||
(void)SysCloseHandle(file);
|
||||
|
||||
uint32_t endpointCount = 0;
|
||||
SYS_AUDIO_ENUMERATE_ENDPOINTS_REQUEST enumerateAudio;
|
||||
memset(&enumerateAudio, 0, sizeof(enumerateAudio));
|
||||
enumerateAudio.Size = sizeof(enumerateAudio);
|
||||
enumerateAudio.CountOut = &endpointCount;
|
||||
status = SysAudioEnumerateEndpoints(&enumerateAudio);
|
||||
if (status != NTLSTATUS_ACCESS_DENIED) return 22;
|
||||
|
||||
SYS_HANDLE queue = SYS_HANDLE_INVALID;
|
||||
status = SysCreateQueue(&queue);
|
||||
if (!KSUCCESS(status)) return 23;
|
||||
SYS_PUBLISH_QUEUE_REQUEST publishQueue = {
|
||||
sizeof(publishQueue), SysSvFromCstr("security_smoke.restricted"), queue
|
||||
};
|
||||
status = SysPublishQueue(&publishQueue);
|
||||
(void)SysCloseHandle(queue);
|
||||
if (status != NTLSTATUS_ACCESS_DENIED) return 24;
|
||||
|
||||
SYS_HANDLE listener = SYS_HANDLE_INVALID;
|
||||
SYS_CREATE_PORT_REQUEST createPort;
|
||||
memset(&createPort, 0, sizeof(createPort));
|
||||
createPort.Size = sizeof(createPort);
|
||||
createPort.HandleOut = &listener;
|
||||
status = SysCreatePort(&createPort);
|
||||
if (!KSUCCESS(status)) return 25;
|
||||
status = SysRegisterSystemService(SYS_SERVICE_RESOURCE, listener);
|
||||
(void)SysCloseHandle(listener);
|
||||
if (status != NTLSTATUS_ACCESS_DENIED) return 26;
|
||||
|
||||
uint32_t selfPid = SysGetCurrentPid();
|
||||
status = SysOpenProcess(selfPid, SYS_RIGHT_QUERY, &process);
|
||||
if (!KSUCCESS(status)) return 27;
|
||||
status = SysCloseHandle(process);
|
||||
if (!KSUCCESS(status)) return 28;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Creates one zero-authority security context from the caller's current delegable root.
|
||||
static SYS_HANDLE derive_stripped_context(void) {
|
||||
SYS_HANDLE handle = 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.Flags = SYS_SECURITY_CONTEXT_FLAG_NONE;
|
||||
request.ContextHandleOut = &handle;
|
||||
KSTATUS status = SysDeriveSecurityContext(&request);
|
||||
if (!KSUCCESS(status)) fail_status("derive stripped context", status);
|
||||
return handle;
|
||||
}
|
||||
|
||||
// Runs the privileged parent half of the security-context smoke test.
|
||||
static int run_parent(void) {
|
||||
SYS_PEB* peb = SysGetPeb();
|
||||
if (!peb || !peb->ImagePath.Data || peb->ImagePath.Length == 0)
|
||||
fail_status("missing image path", NTLSTATUS_FAILURE);
|
||||
|
||||
SYS_SECURITY_CONTEXT_INFO rootInfo;
|
||||
memset(&rootInfo, 0, sizeof(rootInfo));
|
||||
rootInfo.Size = sizeof(rootInfo);
|
||||
KSTATUS status = SysQuerySecurityContext(SYS_SECURITY_CONTEXT_CURRENT, &rootInfo);
|
||||
if (!KSUCCESS(status)) fail_status("query current context", status);
|
||||
if (rootInfo.EffectivePrivileges != SYS_PRIV_ALL ||
|
||||
rootInfo.DelegablePrivileges != SYS_PRIV_ALL ||
|
||||
rootInfo.ServiceClaims != SYS_SERVICE_CLAIM_ALL ||
|
||||
rootInfo.DelegableServiceClaims != SYS_SERVICE_CLAIM_ALL)
|
||||
fail_status("bootstrap context authority", NTLSTATUS_FAILURE);
|
||||
|
||||
SYS_HANDLE restricted = derive_stripped_context();
|
||||
SYS_SECURITY_CONTEXT_INFO restrictedInfo;
|
||||
memset(&restrictedInfo, 0, sizeof(restrictedInfo));
|
||||
restrictedInfo.Size = sizeof(restrictedInfo);
|
||||
status = SysQuerySecurityContext(restricted, &restrictedInfo);
|
||||
if (!KSUCCESS(status)) fail_status("query stripped context", status);
|
||||
if (restrictedInfo.SecurityId <= rootInfo.SecurityId ||
|
||||
restrictedInfo.EffectivePrivileges || restrictedInfo.DelegablePrivileges ||
|
||||
restrictedInfo.ServiceClaims || restrictedInfo.DelegableServiceClaims)
|
||||
fail_status("stripped context contents", NTLSTATUS_FAILURE);
|
||||
|
||||
SYS_HANDLE illegal = SYS_HANDLE_INVALID;
|
||||
SYS_DERIVE_SECURITY_CONTEXT_REQUEST amplify;
|
||||
memset(&lify, 0, sizeof(amplify));
|
||||
amplify.Size = sizeof(amplify);
|
||||
amplify.SourceContext = restricted;
|
||||
amplify.EffectivePrivileges = SYS_PRIV_PROCESS_OPEN_ANY;
|
||||
amplify.ContextHandleOut = &illegal;
|
||||
status = SysDeriveSecurityContext(&lify);
|
||||
if (status != NTLSTATUS_ACCESS_DENIED)
|
||||
fail_status("blocked privilege amplification", status);
|
||||
|
||||
memset(&lify, 0, sizeof(amplify));
|
||||
amplify.Size = sizeof(amplify);
|
||||
amplify.SourceContext = restricted;
|
||||
amplify.ServiceClaims = SYS_SERVICE_CLAIM_RESOURCE;
|
||||
amplify.ContextHandleOut = &illegal;
|
||||
status = SysDeriveSecurityContext(&lify);
|
||||
if (status != NTLSTATUS_ACCESS_DENIED)
|
||||
fail_status("blocked service-claim amplification", status);
|
||||
|
||||
SYS_HANDLE queryOnly = SYS_HANDLE_INVALID;
|
||||
SYS_DERIVE_HANDLE_REQUEST deriveHandle = {
|
||||
sizeof(deriveHandle), restricted, SYS_RIGHT_QUERY, &queryOnly
|
||||
};
|
||||
status = SysDeriveHandle(&deriveHandle);
|
||||
if (!KSUCCESS(status)) fail_status("derive query-only context handle", status);
|
||||
|
||||
SYS_HANDLE rejectedChild = SYS_HANDLE_INVALID;
|
||||
NTL_CREATE_SYSTEM_PROCESS_REQUEST rejectedCreate;
|
||||
memset(&rejectedCreate, 0, sizeof(rejectedCreate));
|
||||
rejectedCreate.Size = sizeof(rejectedCreate);
|
||||
rejectedCreate.Flags = NTL_PROCESS_LOAD_FLAG_NONE;
|
||||
rejectedCreate.Path = peb->ImagePath;
|
||||
rejectedCreate.CommandLine = SysSvFromCstr("restricted:1");
|
||||
rejectedCreate.SecurityContext = queryOnly;
|
||||
rejectedCreate.ResolveModuleName = NtlResolveSystemModuleName;
|
||||
rejectedCreate.ProcessHandleOut = &rejectedChild;
|
||||
status = NtlCreateSystemProcess(&rejectedCreate);
|
||||
if (status != NTLSTATUS_ACCESS_DENIED)
|
||||
fail_status("context assignment right", status);
|
||||
(void)SysCloseHandle(queryOnly);
|
||||
|
||||
char command[48] = "restricted:";
|
||||
size_t commandLength = strlen(command);
|
||||
if (!append_u32(command, sizeof(command), &commandLength, SysGetCurrentPid()))
|
||||
fail_status("build child command", NTLSTATUS_BUFFER_TOO_SMALL);
|
||||
|
||||
SYS_HANDLE child = SYS_HANDLE_INVALID;
|
||||
NTL_CREATE_SYSTEM_PROCESS_REQUEST create;
|
||||
memset(&create, 0, sizeof(create));
|
||||
create.Size = sizeof(create);
|
||||
create.Flags = NTL_PROCESS_LOAD_FLAG_NONE;
|
||||
create.Path = peb->ImagePath;
|
||||
create.CommandLine = SysSvFromCstr(command);
|
||||
create.SecurityContext = restricted;
|
||||
create.ResolveModuleName = NtlResolveSystemModuleName;
|
||||
create.ProcessHandleOut = &child;
|
||||
status = NtlCreateSystemProcess(&create);
|
||||
if (!KSUCCESS(status)) fail_status("create restricted child", status);
|
||||
|
||||
status = SysWait(child, SYS_WAIT_FLAG_NONE);
|
||||
if (!KSUCCESS(status)) fail_status("wait restricted child", status);
|
||||
int32_t childExit = -1;
|
||||
status = SysGetProcessExitCode(child, &childExit);
|
||||
if (!KSUCCESS(status)) fail_status("query restricted child", status);
|
||||
(void)SysCloseHandle(child);
|
||||
if (childExit != 0) {
|
||||
print("security_smoke: restricted child failure=");
|
||||
print_u32((uint32_t)childExit);
|
||||
print("\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
status = SysCloseHandle(restricted);
|
||||
if (!KSUCCESS(status)) fail_status("close stripped context", status);
|
||||
|
||||
SYS_HANDLE later = derive_stripped_context();
|
||||
SYS_SECURITY_CONTEXT_INFO laterInfo;
|
||||
memset(&laterInfo, 0, sizeof(laterInfo));
|
||||
laterInfo.Size = sizeof(laterInfo);
|
||||
status = SysQuerySecurityContext(later, &laterInfo);
|
||||
if (!KSUCCESS(status)) fail_status("query later context", status);
|
||||
if (laterInfo.SecurityId <= restrictedInfo.SecurityId)
|
||||
fail_status("security id reuse", NTLSTATUS_FAILURE);
|
||||
(void)SysCloseHandle(later);
|
||||
|
||||
print("security_smoke: PASS\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Runs either the privileged parent or the stripped child based on the process command line.
|
||||
int main(void) {
|
||||
SYS_PEB* peb = SysGetPeb();
|
||||
if (!peb) return 2;
|
||||
const char prefix[] = "restricted:";
|
||||
if (peb->CommandLine.Length > sizeof(prefix) - 1u &&
|
||||
memcmp(peb->CommandLine.Data, prefix, sizeof(prefix) - 1u) == 0) {
|
||||
uint32_t parentPid = 0;
|
||||
if (!parse_u32(peb->CommandLine, sizeof(prefix) - 1u, &parentPid)) return 3;
|
||||
return run_restricted_child(parentPid);
|
||||
}
|
||||
return run_parent();
|
||||
}
|
||||
Reference in new issue
Block a user