201 lines
8.5 KiB
C
201 lines
8.5 KiB
C
// Resource-service smoke test. Proves public RSSRV lookup is capability-based and namespace administration is private.
|
|
|
|
#include <ntlite/resource.h>
|
|
#include <ntlite/syscall.h>
|
|
#include <ntlite/path.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
static void print(const char* text) {
|
|
SYS_WRITE_CONSOLE_REQUEST request = { sizeof(request), SysSvFromCstr(text) };
|
|
(void)SysWriteConsole(&request);
|
|
}
|
|
|
|
|
|
// Builds one /Device/HarddiskVolumeN resource path.
|
|
static SYS_STRING_VIEW make_volume_path(uint32_t index, char* buffer, size_t capacity) {
|
|
static const char prefix[] = "/Device/HarddiskVolume";
|
|
size_t prefixLength = sizeof(prefix) - 1u;
|
|
if (!index || !buffer || capacity <= prefixLength)
|
|
return SysSvFromParts((const char*)0, 0);
|
|
|
|
char digits[10];
|
|
size_t digitCount = 0;
|
|
do {
|
|
digits[digitCount++] = (char)('0' + (index % 10u));
|
|
index /= 10u;
|
|
} while (index && digitCount < sizeof(digits));
|
|
if (prefixLength + digitCount + 1u > capacity)
|
|
return SysSvFromParts((const char*)0, 0);
|
|
|
|
memcpy(buffer, prefix, prefixLength);
|
|
for (size_t i = 0; i < digitCount; ++i)
|
|
buffer[prefixLength + i] = digits[digitCount - 1u - i];
|
|
buffer[prefixLength + digitCount] = '\0';
|
|
return SysSvFromParts(buffer, prefixLength + digitCount);
|
|
}
|
|
|
|
static int fail(const char* what, KSTATUS status) {
|
|
(void)status;
|
|
print("resource_service_smoke: FAIL ");
|
|
print(what);
|
|
print("\n");
|
|
return 1;
|
|
}
|
|
|
|
int main(void) {
|
|
SYS_HANDLE session = SYS_HANDLE_INVALID;
|
|
KSTATUS status = NtlResourceConnect(&session);
|
|
if (!KSUCCESS(status)) return fail("connect", status);
|
|
|
|
uint32_t kind = NTL_RESOURCE_KIND_INVALID;
|
|
status = NtlResourceQuery(session, SYS_SV_LITERAL("/SystemRoot"), &kind);
|
|
if (!KSUCCESS(status) || kind != NTL_RESOURCE_KIND_VOLUME)
|
|
return fail("query /SystemRoot", status);
|
|
|
|
// RSSRV assigns its own monotonic /Device/HarddiskVolumeN names to mounted IO_VOLUME instances.
|
|
uint32_t publishedVolumes = 0;
|
|
uint32_t systemVolumePublished = 0;
|
|
for (uint32_t index = 1; index <= 32; ++index) {
|
|
char volumePathBuffer[48];
|
|
SYS_STRING_VIEW volumePath = make_volume_path(index, volumePathBuffer, sizeof(volumePathBuffer));
|
|
if (!volumePath.Data)
|
|
return fail("build dynamic volume path", NTLSTATUS_FAILURE);
|
|
|
|
SYS_HANDLE published = SYS_HANDLE_INVALID;
|
|
status = NtlResourceLookup(session, volumePath, SYS_RIGHT_QUERY | SYS_VOLUME_RIGHT_OPEN, &published);
|
|
if (status == NTLSTATUS_NOT_FOUND)
|
|
continue;
|
|
if (!KSUCCESS(status))
|
|
return fail("lookup dynamic volume", status);
|
|
publishedVolumes++;
|
|
|
|
SYS_HANDLE probe = SYS_HANDLE_INVALID;
|
|
SYS_OPEN_FILE_AT_REQUEST probeOpen;
|
|
memset(&probeOpen, 0, sizeof(probeOpen));
|
|
probeOpen.Size = sizeof(probeOpen);
|
|
probeOpen.RootHandle = published;
|
|
probeOpen.Path = SYS_SV_LITERAL("system/init.exe");
|
|
probeOpen.Access = SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY;
|
|
probeOpen.Disposition = SYS_FILE_OPEN_EXISTING;
|
|
probeOpen.Flags = SYS_FILE_FLAG_NONE;
|
|
probeOpen.HandleOut = &probe;
|
|
KSTATUS probeStatus = SysOpenFileAt(&probeOpen);
|
|
if (KSUCCESS(probeStatus)) {
|
|
systemVolumePublished = 1;
|
|
(void)SysCloseHandle(probe);
|
|
} else if (probeStatus != NTLSTATUS_NOT_FOUND) {
|
|
(void)SysCloseHandle(published);
|
|
return fail("probe dynamic volume", probeStatus);
|
|
}
|
|
(void)SysCloseHandle(published);
|
|
}
|
|
if (!publishedVolumes || !systemVolumePublished)
|
|
return fail("dynamic volume publication", NTLSTATUS_NOT_FOUND);
|
|
|
|
SYS_HANDLE volume = SYS_HANDLE_INVALID;
|
|
status = NtlResourceLookup(session, SYS_SV_LITERAL("/SystemRoot"),
|
|
SYS_RIGHT_QUERY | SYS_VOLUME_RIGHT_OPEN, &volume);
|
|
if (!KSUCCESS(status)) return fail("lookup /SystemRoot", status);
|
|
|
|
SYS_HANDLE file = SYS_HANDLE_INVALID;
|
|
SYS_OPEN_FILE_AT_REQUEST open;
|
|
memset(&open, 0, sizeof(open));
|
|
open.Size = sizeof(open);
|
|
open.RootHandle = volume;
|
|
open.Path = SYS_SV_LITERAL("system/init.exe");
|
|
open.Access = SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY;
|
|
open.Disposition = SYS_FILE_OPEN_EXISTING;
|
|
open.Flags = SYS_FILE_FLAG_NONE;
|
|
open.HandleOut = &file;
|
|
status = SysOpenFileAt(&open);
|
|
if (!KSUCCESS(status)) return fail("open through resource volume", status);
|
|
(void)SysCloseHandle(file);
|
|
(void)SysCloseHandle(volume);
|
|
|
|
// RESOLVE returns the longest bound resource prefix and leaves only a relative tail for I/O.
|
|
SYS_HANDLE resolvedRoot = SYS_HANDLE_INVALID;
|
|
uint32_t resolvedLength = 0;
|
|
status = NtlResourceResolve(session, SYS_SV_LITERAL("/SystemRoot/system/init.exe"),
|
|
NTL_RESOURCE_FLAG_NONE, &resolvedRoot, &kind, &resolvedLength);
|
|
if (!KSUCCESS(status) || kind != NTL_RESOURCE_KIND_VOLUME ||
|
|
resolvedLength != sizeof("/SystemRoot") - 1u)
|
|
return fail("resolve longest prefix", status);
|
|
|
|
file = SYS_HANDLE_INVALID;
|
|
memset(&open, 0, sizeof(open));
|
|
open.Size = sizeof(open);
|
|
open.RootHandle = resolvedRoot;
|
|
open.Path = SYS_SV_LITERAL("system/init.exe");
|
|
open.Access = SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY;
|
|
open.Disposition = SYS_FILE_OPEN_EXISTING;
|
|
open.Flags = SYS_FILE_FLAG_NONE;
|
|
open.HandleOut = &file;
|
|
status = SysOpenFileAt(&open);
|
|
if (!KSUCCESS(status)) return fail("open through resolved prefix", status);
|
|
(void)SysCloseHandle(file);
|
|
(void)SysCloseHandle(resolvedRoot);
|
|
|
|
// MODIFY resolution returns write authority without exporting RSSRV's private TRANSFER right.
|
|
resolvedRoot = SYS_HANDLE_INVALID;
|
|
resolvedLength = 0;
|
|
status = NtlResourceResolve(session, SYS_SV_LITERAL("/SystemRoot/tests/resource_f.tmp"),
|
|
NTL_RESOURCE_RESOLVE_FLAG_MODIFY,
|
|
&resolvedRoot, &kind, &resolvedLength);
|
|
if (!KSUCCESS(status) || kind != NTL_RESOURCE_KIND_VOLUME ||
|
|
resolvedLength != sizeof("/SystemRoot") - 1u)
|
|
return fail("resolve modify authority", status);
|
|
|
|
file = SYS_HANDLE_INVALID;
|
|
memset(&open, 0, sizeof(open));
|
|
open.Size = sizeof(open);
|
|
open.RootHandle = resolvedRoot;
|
|
open.Path = SYS_SV_LITERAL("tests/resource_f.tmp");
|
|
open.Access = SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_QUERY;
|
|
open.Disposition = SYS_FILE_CREATE_ALWAYS;
|
|
open.Flags = SYS_FILE_FLAG_NONE;
|
|
open.HandleOut = &file;
|
|
status = SysOpenFileAt(&open);
|
|
if (!KSUCCESS(status)) return fail("create through resolved modify root", status);
|
|
(void)SysCloseHandle(file);
|
|
|
|
SYS_PATH_AT_REQUEST remove = { sizeof(remove), resolvedRoot, SYS_SV_LITERAL("tests/resource_f.tmp") };
|
|
status = SysDeleteAt(&remove);
|
|
if (!KSUCCESS(status)) return fail("delete through resolved modify root", status);
|
|
(void)SysCloseHandle(resolvedRoot);
|
|
|
|
SYS_HANDLE overbroad = SYS_HANDLE_INVALID;
|
|
status = NtlResourceLookup(session, SYS_SV_LITERAL("/SystemRoot"),
|
|
SYS_RIGHT_QUERY | SYS_RIGHT_TRANSFER | SYS_VOLUME_RIGHT_OPEN, &overbroad);
|
|
if (status != NTLSTATUS_ACCESS_DENIED || overbroad != SYS_HANDLE_INVALID)
|
|
return fail("export-rights ceiling", status);
|
|
|
|
SYS_HANDLE missing = SYS_HANDLE_INVALID;
|
|
status = NtlResourceLookup(session, SYS_SV_LITERAL("/Missing"), SYS_RIGHT_QUERY, &missing);
|
|
if (status != NTLSTATUS_NOT_FOUND || missing != SYS_HANDLE_INVALID)
|
|
return fail("missing lookup", status);
|
|
|
|
SYS_HANDLE event = SYS_HANDLE_INVALID;
|
|
status = SysCreateEvent(0, 0, &event);
|
|
if (!KSUCCESS(status)) return fail("create bind probe", status);
|
|
status = NtlResourceBind(session, SYS_SV_LITERAL("/PublicBindMustFail"),
|
|
NTL_RESOURCE_KIND_CAPABILITY, event,
|
|
SYS_RIGHT_TRANSFER | SYS_RIGHT_QUERY, SYS_RIGHT_QUERY);
|
|
(void)SysCloseHandle(event);
|
|
if (status != NTLSTATUS_ACCESS_DENIED)
|
|
return fail("public bind authority", status);
|
|
|
|
status = NtlResourceQuery(session, SYS_SV_LITERAL("/PublicBindMustFail"), &kind);
|
|
if (status != NTLSTATUS_NOT_FOUND)
|
|
return fail("rejected bind visibility", status);
|
|
|
|
status = NtlResourceQuery(session, SYS_SV_LITERAL("/SystemRoot/../Bad"), &kind);
|
|
if (status != NTLSTATUS_INVALID_PARAMETER)
|
|
return fail("canonical path rejection", status);
|
|
|
|
status = NtlResourceClose(session);
|
|
if (!KSUCCESS(status)) return fail("close session", status);
|
|
print("resource_service_smoke: PASS\n");
|
|
return 0;
|
|
}
|