Files
nt-lite/code/user/syscall/resource.c
T

309 lines
13 KiB
C

// Native resource-service client. Resource naming policy stays in rssrv; this file only speaks its Port protocol.
#include <ntlite/resource.h>
#include <ntlite/service.h>
#include <ntlite/syscall.h>
#include <string.h>
// Validates the canonical global resource-path syntax understood by rssrv.
static KSTATUS NtlValidateResourcePath(SYS_STRING_VIEW path) {
if (!path.Data || path.Length < 2 || path.Length > NTL_RESOURCE_PATH_MAX || path.Data[0] != '/')
return NTLSTATUS_INVALID_PARAMETER;
if (path.Data[path.Length - 1] == '/')
return NTLSTATUS_INVALID_PARAMETER;
size_t componentStart = 1;
for (size_t i = 1; i <= path.Length; ++i) {
if (i < path.Length) {
char c = path.Data[i];
if (c == '\\' || c == '\0')
return NTLSTATUS_INVALID_PARAMETER;
if (c != '/')
continue;
}
size_t componentLength = i - componentStart;
if (componentLength == 0 ||
(componentLength == 1 && path.Data[componentStart] == '.') ||
(componentLength == 2 && path.Data[componentStart] == '.' && path.Data[componentStart + 1] == '.'))
return NTLSTATUS_INVALID_PARAMETER;
componentStart = i + 1;
}
return NTLSTATUS_SUCCESS;
}
// Builds one inline request header and path payload.
static KSTATUS NtlBuildResourceRequest(uint8_t* buffer,
size_t bufferSize,
uint32_t operation,
SYS_STRING_VIEW path,
SYS_RIGHTS rights,
SYS_RIGHTS exportRights,
uint32_t kind,
uint32_t* lengthOut) {
KSTATUS status = NtlValidateResourcePath(path);
if (!KSUCCESS(status) || !buffer || !lengthOut)
return KSUCCESS(status) ? NTLSTATUS_INVALID_PARAMETER : status;
size_t total = sizeof(NTL_RESOURCE_REQUEST) + path.Length;
if (total > bufferSize || total > SYS_PORT_MAX_INLINE_BYTES)
return NTLSTATUS_BUFFER_TOO_SMALL;
NTL_RESOURCE_REQUEST header;
memset(&header, 0, sizeof(header));
header.Size = sizeof(header);
header.Version = NTL_RESOURCE_PROTOCOL_VERSION;
header.Operation = operation;
header.Flags = NTL_RESOURCE_FLAG_NONE;
header.Rights = rights;
header.ExportRights = exportRights;
header.Kind = kind;
header.PathLength = (uint32_t)path.Length;
memcpy(buffer, &header, sizeof(header));
memcpy(buffer + sizeof(header), path.Data, path.Length);
*lengthOut = (uint32_t)total;
return NTLSTATUS_SUCCESS;
}
// Executes one resource request and validates its fixed protocol reply.
static KSTATUS NtlCallResource(SYS_HANDLE session,
const void* requestPayload,
uint32_t requestLength,
const SYS_PORT_HANDLE_TRANSFER* requestHandles,
uint32_t requestHandleCount,
NTL_RESOURCE_REPLY* replyOut,
SYS_HANDLE* replyHandleOut) {
if (session == SYS_HANDLE_INVALID || !requestPayload || !requestLength || !replyOut)
return NTLSTATUS_INVALID_PARAMETER;
NTL_RESOURCE_REPLY reply;
memset(&reply, 0, sizeof(reply));
SYS_PORT_MESSAGE_INFO info;
memset(&info, 0, sizeof(info));
info.Size = sizeof(info);
SYS_HANDLE replyHandle = SYS_HANDLE_INVALID;
SYS_CALL_PORT_REQUEST call;
memset(&call, 0, sizeof(call));
call.Size = sizeof(call);
call.EndpointHandle = session;
call.Flags = SYS_PORT_FLAG_NONE;
call.RequestPayload = requestPayload;
call.RequestLength = requestLength;
call.RequestHandles = requestHandles;
call.RequestHandleCount = requestHandleCount;
call.ReplyPayload = &reply;
call.ReplyCapacity = sizeof(reply);
call.ReplyHandlesOut = replyHandleOut ? &replyHandle : (SYS_HANDLE*)0;
call.ReplyHandleCapacity = replyHandleOut ? 1u : 0u;
call.ReplyInfoOut = &info;
call.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
KSTATUS status = SysCallPort(&call);
if (!KSUCCESS(status))
return status;
if (info.Type != SYS_PORT_MESSAGE_REPLY || info.PayloadLength != sizeof(reply) ||
reply.Size != sizeof(reply) || reply.Version != NTL_RESOURCE_PROTOCOL_VERSION) {
if (replyHandle != SYS_HANDLE_INVALID) (void)SysCloseHandle(replyHandle);
return NTLSTATUS_FAILURE;
}
if ((!replyHandleOut && info.HandleCount != 0) || (replyHandleOut && info.HandleCount > 1)) {
if (replyHandle != SYS_HANDLE_INVALID) (void)SysCloseHandle(replyHandle);
return NTLSTATUS_FAILURE;
}
*replyOut = reply;
if (replyHandleOut) *replyHandleOut = replyHandle;
return NTLSTATUS_SUCCESS;
}
// Connects to the registered resource listener.
KSTATUS NtlResourceConnect(SYS_HANDLE* sessionOut) {
if (!sessionOut) return NTLSTATUS_INVALID_PARAMETER;
*sessionOut = SYS_HANDLE_INVALID;
SYS_CONNECT_SYSTEM_SERVICE_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.ServiceId = SYS_SERVICE_RESOURCE;
request.EndpointHandleOut = sessionOut;
return SysConnectSystemService(&request);
}
// Resolves the canonical /SystemRoot resource and closes the transient resource session.
KSTATUS NtlResourceOpenSystemRoot(SYS_RIGHTS desiredRights, SYS_HANDLE* handleOut) {
if (!desiredRights || !handleOut) return NTLSTATUS_INVALID_PARAMETER;
*handleOut = SYS_HANDLE_INVALID;
SYS_HANDLE session = SYS_HANDLE_INVALID;
KSTATUS status = NtlResourceConnect(&session);
if (!KSUCCESS(status)) return status;
status = NtlResourceLookup(session, SysSvFromCstr("/SystemRoot"), desiredRights, handleOut);
(void)NtlResourceClose(session);
return status;
}
// Connects through one delegated resource-listener capability.
KSTATUS NtlResourceConnectListener(SYS_HANDLE listenerHandle, SYS_HANDLE* sessionOut) {
if (listenerHandle == SYS_HANDLE_INVALID || !sessionOut)
return NTLSTATUS_INVALID_PARAMETER;
*sessionOut = SYS_HANDLE_INVALID;
SYS_CONNECT_PORT_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.ListenerHandle = listenerHandle;
request.Flags = SYS_PORT_CONNECT_FLAG_NONE;
request.EndpointHandleOut = sessionOut;
return SysConnectPort(&request);
}
// Looks up one resource and requests an attenuated alias of its bound capability.
KSTATUS NtlResourceLookup(SYS_HANDLE session,
SYS_STRING_VIEW path,
SYS_RIGHTS desiredRights,
SYS_HANDLE* handleOut) {
if (!desiredRights || !handleOut) return NTLSTATUS_INVALID_PARAMETER;
*handleOut = SYS_HANDLE_INVALID;
uint8_t payload[sizeof(NTL_RESOURCE_REQUEST) + NTL_RESOURCE_PATH_MAX];
uint32_t payloadLength = 0;
KSTATUS status = NtlBuildResourceRequest(payload, sizeof(payload), NTL_RESOURCE_OP_LOOKUP,
path, desiredRights, 0, NTL_RESOURCE_KIND_INVALID,
&payloadLength);
if (!KSUCCESS(status)) return status;
NTL_RESOURCE_REPLY reply;
SYS_HANDLE handle = SYS_HANDLE_INVALID;
status = NtlCallResource(session, payload, payloadLength, (const SYS_PORT_HANDLE_TRANSFER*)0, 0,
&reply, &handle);
if (!KSUCCESS(status)) return status;
if (reply.Operation != NTL_RESOURCE_OP_LOOKUP) {
if (handle != SYS_HANDLE_INVALID) (void)SysCloseHandle(handle);
return NTLSTATUS_FAILURE;
}
if (!KSUCCESS(reply.Status)) {
if (handle != SYS_HANDLE_INVALID) (void)SysCloseHandle(handle);
return reply.Status;
}
if (handle == SYS_HANDLE_INVALID) return NTLSTATUS_FAILURE;
*handleOut = handle;
return NTLSTATUS_SUCCESS;
}
// Queries one bound resource kind without transferring authority.
KSTATUS NtlResourceQuery(SYS_HANDLE session, SYS_STRING_VIEW path, uint32_t* kindOut) {
if (!kindOut) return NTLSTATUS_INVALID_PARAMETER;
*kindOut = NTL_RESOURCE_KIND_INVALID;
uint8_t payload[sizeof(NTL_RESOURCE_REQUEST) + NTL_RESOURCE_PATH_MAX];
uint32_t payloadLength = 0;
KSTATUS status = NtlBuildResourceRequest(payload, sizeof(payload), NTL_RESOURCE_OP_QUERY,
path, 0, 0, NTL_RESOURCE_KIND_INVALID, &payloadLength);
if (!KSUCCESS(status)) return status;
NTL_RESOURCE_REPLY reply;
status = NtlCallResource(session, payload, payloadLength, (const SYS_PORT_HANDLE_TRANSFER*)0, 0,
&reply, (SYS_HANDLE*)0);
if (!KSUCCESS(status)) return status;
if (reply.Operation != NTL_RESOURCE_OP_QUERY) return NTLSTATUS_FAILURE;
if (!KSUCCESS(reply.Status)) return reply.Status;
*kindOut = reply.Kind;
return NTLSTATUS_SUCCESS;
}
// Resolves the longest bound resource prefix into one traversal capability.
KSTATUS NtlResourceResolve(SYS_HANDLE session,
SYS_STRING_VIEW path,
uint32_t flags,
SYS_HANDLE* rootHandleOut,
uint32_t* kindOut,
uint32_t* resolvedLengthOut) {
if (!rootHandleOut || !kindOut || !resolvedLengthOut ||
(flags & ~NTL_RESOURCE_RESOLVE_FLAG_MODIFY) != 0)
return NTLSTATUS_INVALID_PARAMETER;
*rootHandleOut = SYS_HANDLE_INVALID;
*kindOut = NTL_RESOURCE_KIND_INVALID;
*resolvedLengthOut = 0;
uint8_t payload[sizeof(NTL_RESOURCE_REQUEST) + NTL_RESOURCE_PATH_MAX];
uint32_t payloadLength = 0;
KSTATUS status = NtlBuildResourceRequest(payload, sizeof(payload), NTL_RESOURCE_OP_RESOLVE,
path, 0, 0, NTL_RESOURCE_KIND_INVALID, &payloadLength);
if (!KSUCCESS(status)) return status;
((NTL_RESOURCE_REQUEST*)payload)->Flags = flags;
NTL_RESOURCE_REPLY reply;
SYS_HANDLE handle = SYS_HANDLE_INVALID;
status = NtlCallResource(session, payload, payloadLength, (const SYS_PORT_HANDLE_TRANSFER*)0, 0,
&reply, &handle);
if (!KSUCCESS(status)) return status;
if (reply.Operation != NTL_RESOURCE_OP_RESOLVE || reply.Flags != flags) {
if (handle != SYS_HANDLE_INVALID) (void)SysCloseHandle(handle);
return NTLSTATUS_FAILURE;
}
if (!KSUCCESS(reply.Status)) {
if (handle != SYS_HANDLE_INVALID) (void)SysCloseHandle(handle);
return reply.Status;
}
if (handle == SYS_HANDLE_INVALID || reply.Kind == NTL_RESOURCE_KIND_INVALID ||
reply.ResolvedLength < 2 || reply.ResolvedLength > path.Length ||
(reply.ResolvedLength != path.Length && path.Data[reply.ResolvedLength] != '/')) {
if (handle != SYS_HANDLE_INVALID) (void)SysCloseHandle(handle);
return NTLSTATUS_FAILURE;
}
*rootHandleOut = handle;
*kindOut = reply.Kind;
*resolvedLengthOut = reply.ResolvedLength;
return NTLSTATUS_SUCCESS;
}
// Binds an explicitly attenuated capability into an administrative resource session.
KSTATUS NtlResourceBind(SYS_HANDLE session,
SYS_STRING_VIEW path,
uint32_t kind,
SYS_HANDLE resourceHandle,
SYS_RIGHTS namespaceRights,
SYS_RIGHTS exportRights) {
if (kind == NTL_RESOURCE_KIND_INVALID || resourceHandle == SYS_HANDLE_INVALID ||
!(namespaceRights & SYS_RIGHT_TRANSFER) || !exportRights ||
(exportRights & ~namespaceRights) != 0)
return NTLSTATUS_INVALID_PARAMETER;
uint8_t payload[sizeof(NTL_RESOURCE_REQUEST) + NTL_RESOURCE_PATH_MAX];
uint32_t payloadLength = 0;
KSTATUS status = NtlBuildResourceRequest(payload, sizeof(payload), NTL_RESOURCE_OP_BIND,
path, namespaceRights, exportRights, kind, &payloadLength);
if (!KSUCCESS(status)) return status;
SYS_PORT_HANDLE_TRANSFER transfer;
transfer.Handle = resourceHandle;
transfer.DesiredRights = namespaceRights;
transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE;
NTL_RESOURCE_REPLY reply;
status = NtlCallResource(session, payload, payloadLength, &transfer, 1, &reply, (SYS_HANDLE*)0);
if (!KSUCCESS(status)) return status;
if (reply.Operation != NTL_RESOURCE_OP_BIND) return NTLSTATUS_FAILURE;
return reply.Status;
}
// Removes one resource name through an administrative session.
KSTATUS NtlResourceUnbind(SYS_HANDLE session, SYS_STRING_VIEW path) {
uint8_t payload[sizeof(NTL_RESOURCE_REQUEST) + NTL_RESOURCE_PATH_MAX];
uint32_t payloadLength = 0;
KSTATUS status = NtlBuildResourceRequest(payload, sizeof(payload), NTL_RESOURCE_OP_UNBIND,
path, 0, 0, NTL_RESOURCE_KIND_INVALID, &payloadLength);
if (!KSUCCESS(status)) return status;
NTL_RESOURCE_REPLY reply;
status = NtlCallResource(session, payload, payloadLength, (const SYS_PORT_HANDLE_TRANSFER*)0, 0,
&reply, (SYS_HANDLE*)0);
if (!KSUCCESS(status)) return status;
if (reply.Operation != NTL_RESOURCE_OP_UNBIND) return NTLSTATUS_FAILURE;
return reply.Status;
}
// Disconnects and closes one resource session.
KSTATUS NtlResourceClose(SYS_HANDLE session) {
if (session == SYS_HANDLE_INVALID) return NTLSTATUS_INVALID_HANDLE;
KSTATUS disconnect = SysDisconnectPort(session);
KSTATUS close = SysCloseHandle(session);
if (!KSUCCESS(disconnect) && disconnect != NTLSTATUS_PORT_DISCONNECTED)
return disconnect;
return close;
}