Blob Blame History Raw
/*********************************************************/
/*  toksvc: a framework-native token broker service      */
/*  Copyright (C) 2020  SysDeer Technologies, LLC        */
/*  Released under GPLv2 and GPLv3; see COPYING.TOKSVC.  */
/*********************************************************/

#include <psxtypes/psxtypes.h>
#include <ntapi/ntapi.h>
#include <ntapi/nt_termios.h>

#include "toksvc_daemon_impl.h"
#include "toksvc_driver_impl.h"
#include "toksvc_log_impl.h"

static int32_t toks_daemon_ioctl_get_token_count(struct toks_daemon_ctx * dctx)
{
	nt_tty_port_msg * msg = &dctx->reply;
	msg->ttyinfo.exarg = (void *)(intptr_t)toks_get_driver_ntokens(dctx->driver_ctx);
	return NT_STATUS_SUCCESS;
}

static int32_t toks_daemon_ioctl_set_token_count(struct toks_daemon_ctx * dctx)
{
	nt_tty_port_msg * msg;
	int               ntokens;

	msg     = &dctx->reply;
	ntokens = (int)(intptr_t)msg->ttyinfo.exarg;

	if (ntokens < 0)
		return NT_STATUS_INVALID_PARAMETER;

	else if (ntokens > toks_get_driver_atokens(dctx->driver_ctx))
		return NT_STATUS_BUFFER_TOO_SMALL;

	toks_set_driver_ntokens(dctx->driver_ctx,ntokens);

	dctx->ftokens = (dctx->utokens < ntokens)
		? ntokens - dctx->utokens : 0;

	return toks_daemon_release(dctx);
}

static int32_t toks_daemon_ioctl_get_log_level(struct toks_daemon_ctx * dctx)
{
	nt_tty_port_msg * msg = &dctx->reply;
	msg->ttyinfo.exarg = (void *)(intptr_t)dctx->driver_ctx->cctx->loglevel;
	return NT_STATUS_SUCCESS;
}

static int32_t toks_daemon_ioctl_set_log_level(struct toks_daemon_ctx * dctx)
{
	nt_tty_port_msg * msg;
	int               loglevel;

	msg      = &dctx->reply;
	loglevel = (int)(intptr_t)msg->ttyinfo.exarg;

	if ((loglevel < 0) || (loglevel > 9))
		return NT_STATUS_INVALID_PARAMETER;

	toks_set_driver_log_level(dctx->driver_ctx,loglevel);

	toks_log_service_info(dctx);

	return NT_STATUS_SUCCESS;
}

static int32_t toks_daemon_ioctl_get_service_info(struct toks_daemon_ctx * dctx)
{
	nt_tty_port_msg * msg = &dctx->reply;

	/* uuid */
	msg->svcdata.meta[0]  = (dctx->driver_ctx->cctx->uuid->data1);

	msg->svcdata.meta[1]  = (dctx->driver_ctx->cctx->uuid->data2);
	msg->svcdata.meta[1] |= (dctx->driver_ctx->cctx->uuid->data3 << 16);

	msg->svcdata.meta[2]  = (dctx->driver_ctx->cctx->uuid->data4[0]);
	msg->svcdata.meta[2] |= (dctx->driver_ctx->cctx->uuid->data4[1] << 8);
	msg->svcdata.meta[2] |= (dctx->driver_ctx->cctx->uuid->data4[2] << 16);
	msg->svcdata.meta[2] |= (dctx->driver_ctx->cctx->uuid->data4[3] << 24);

	msg->svcdata.meta[3]  = (dctx->driver_ctx->cctx->uuid->data4[4]);
	msg->svcdata.meta[3] |= (dctx->driver_ctx->cctx->uuid->data4[5] << 8);
	msg->svcdata.meta[3] |= (dctx->driver_ctx->cctx->uuid->data4[6] << 16);
	msg->svcdata.meta[3] |= (dctx->driver_ctx->cctx->uuid->data4[7] << 24);

	/* syspid, systid */
	msg->svcdata.data[0]  = pe_get_current_process_id();
	msg->svcdata.data[1]  = pe_get_current_thread_id();

	/* ctrlpid, csyspid */
	msg->svcdata.data[2]  = dctx->ctrlpid;
	msg->svcdata.data[3]  = dctx->csyspid;

	/* allocated, available, pending */
	msg->svcdata.data[4]  = toks_get_driver_atokens(dctx->driver_ctx);
	msg->svcdata.data[5]  = toks_get_driver_ntokens(dctx->driver_ctx);
	msg->svcdata.data[6]  = toks_get_driver_nwaiters(dctx->driver_ctx);

	/* used, free */
	msg->svcdata.data[7]  = dctx->utokens;
	msg->svcdata.data[8]  = dctx->ftokens;

	return NT_STATUS_SUCCESS;
}

static int32_t toks_daemon_ioctl_log_service_info(struct toks_daemon_ctx * dctx)
{
	toks_log_service_info(dctx);
	return NT_STATUS_SUCCESS;
}

static int32_t toks_daemon_ioctl_get_token_info(struct toks_daemon_ctx * dctx)
{
	int32_t                     status;
	void *                      hprocess;
	void *                      hfile;
	void *                      hrfile;
	void *                      addr;
	char *                      mark;
	size_t                      size;
	int32_t                     idx;
	nt_oa                       oa;
	nt_sd_common_buffer         sd;
	nt_iosb                     iosb;
	nt_unicode_string           path;
	nt_large_integer            asize;
	struct toks_token *         token;
	struct toks_waiter *        waiter;
	struct toks_token_ctx *     tctx;
	struct toks_token_info *    tinfo;
	struct toks_token_sctx      stctx;
	uint32_t                    access;
	uint32_t                    access_owner;
	uint32_t                    access_group;
	uint32_t                    access_other;
	uint32_t                    access_admin;
	int32_t                     nwaiters;
	uint32_t                    keys[4];
	wchar16_t                   name[37];

	/* service info */
	toks_daemon_ioctl_get_service_info(dctx);

	/* hprocess */
	oa.len      = sizeof(nt_oa);
	oa.root_dir = 0;
	oa.obj_name = 0;
	oa.obj_attr = 0;
	oa.sec_desc = 0;
	oa.sec_qos  = 0;

	if ((status = ntapi->zw_open_process(
			&hprocess,
			NT_PROCESS_SYNCHRONIZE
				| NT_PROCESS_DUP_HANDLE
				| NT_PROCESS_QUERY_INFORMATION,
			&oa,&dctx->request.header.client_id)))
		return status;

	/* sd */
	access_owner  = NT_GENERIC_READ     | NT_GENERIC_WRITE;
	access_group  = NT_SEC_READ_CONTROL | NT_FILE_READ_ATTRIBUTES;
	access_other  = NT_SEC_READ_CONTROL | NT_FILE_READ_ATTRIBUTES;

	access_owner |= NT_SEC_STANDARD_RIGHTS_ALL;
	access_admin  = access_owner;

	ntapi->acl_init_common_descriptor(
		&sd,0,0,0,0,
		access_owner,access_group,access_other,
		access_admin,access_owner,
		0);

	/* name */
	toks_query_performance_counters(
		dctx->driver_ctx,
		&dctx->pcnt);

	keys[0] = ntapi->tt_buffer_crc32(
		dctx->noise,
		&dctx->pcnt,sizeof(dctx->pcnt));

	keys[1] = ntapi->tt_buffer_crc32(
		keys[0],
		&dctx->request.header,sizeof(dctx->request.header));

	keys[2] = ntapi->tt_buffer_crc32(
		keys[1],
		toks_get_driver_tokens(dctx->driver_ctx),
		toks_get_driver_ntokens(dctx->driver_ctx)
		* sizeof(struct toks_token));

	keys[3] = ntapi->tt_buffer_crc32(
		keys[2],
		dctx,sizeof(*dctx));

	name[0]  = '.';
	name[9]  = '.';
	name[18] = '.';
	name[27] = '.';
	name[36] = 0;

	ntapi->tt_uint32_to_hex_utf16(keys[0],&name[1]);
	ntapi->tt_uint32_to_hex_utf16(keys[1],&name[10]);
	ntapi->tt_uint32_to_hex_utf16(keys[2],&name[19]);
	ntapi->tt_uint32_to_hex_utf16(keys[3],&name[28]);

	/* path */
	path.strlen = 36 * sizeof(wchar16_t);
	path.maxlen = 0;
	path.buffer = name;

	/* oa */
	oa.len      = sizeof(nt_oa);
	oa.root_dir = toks_get_driver_tmpfs(dctx->driver_ctx);
	oa.obj_name = &path;
	oa.obj_attr = 0;
	oa.sec_desc = &sd.sd;
	oa.sec_qos  = 0;

	/* access */
	access  = NT_SEC_SYNCHRONIZE | NT_SEC_DELETE;
	access |= NT_FILE_READ_DATA  | NT_FILE_READ_ATTRIBUTES;
	access |= NT_FILE_WRITE_DATA | NT_FILE_WRITE_ATTRIBUTES;

	/* alloc_size */
	nwaiters    = toks_get_driver_nwaiters(dctx->driver_ctx);

	asize.quad  = __offsetof(struct toks_token_ctx,tokens);
	asize.quad += sizeof(struct toks_token_info) * dctx->utokens;
	asize.quad += sizeof(struct toks_token_info) * nwaiters;

	/* create temporary file */
	status =  ntapi->zw_create_file(
		&hfile,
		access,&oa,&iosb,&asize,
		NT_FILE_ATTRIBUTE_NORMAL,
		0,NT_FILE_CREATE,
		NT_FILE_NON_DIRECTORY_FILE
			| NT_FILE_SYNCHRONOUS_IO_ALERT
			| NT_FILE_DELETE_ON_CLOSE,
		0,0);

	if (status) {
		ntapi->zw_close(hprocess);
		return status;
	}

	/* buffer */
	if ((dctx->utokens + nwaiters) <= 512) {
		tctx  = 0;
		addr  = &stctx;
		size  = asize.quad;
		tinfo = stctx.tokens;

		stctx.allocated = toks_get_driver_atokens(dctx->driver_ctx);
		stctx.available = toks_get_driver_ntokens(dctx->driver_ctx);
		stctx.pending   = nwaiters;
		stctx.tused     = dctx->utokens;
		stctx.tfree     = dctx->ftokens;
	} else {
		size = asize.quad;
		addr = toks_calloc(1,size);

		if (!addr) {
			ntapi->zw_close(hfile);
			ntapi->zw_close(hprocess);
			return NT_STATUS_MEMORY_NOT_ALLOCATED;
		}

		tctx  = (struct toks_token_ctx *)addr;
		tinfo = tctx->tokens;

		tctx->allocated = toks_get_driver_atokens(dctx->driver_ctx);
		tctx->available = toks_get_driver_ntokens(dctx->driver_ctx);
		tctx->pending   = nwaiters;
		tctx->tused     = dctx->utokens;
		tctx->tfree     = dctx->ftokens;
	}

	/* token info */
	token = toks_get_driver_tokens(dctx->driver_ctx);

	for (idx=0; idx<dctx->utokens; token++) {
		if (token->self) {
			tinfo->tokpid  = token->client.tokpid;
			tinfo->tsyspid = token->client.cid.process_id;
			tinfo->pad     = 0;

			tinfo->keys[0] = token->keys.key[0];
			tinfo->keys[1] = token->keys.key[1];
			tinfo->keys[2] = token->keys.key[2];
			tinfo->keys[3] = token->keys.key[3];
			tinfo->keys[4] = token->keys.key[4];
			tinfo->keys[5] = token->keys.key[5];

			tinfo->meta[0] = token->meta.key[0];
			tinfo->meta[1] = token->meta.key[1];
			tinfo->meta[2] = token->meta.key[2];
			tinfo->meta[3] = token->meta.key[3];
			tinfo->meta[4] = token->meta.key[4];
			tinfo->meta[5] = token->meta.key[5];

			idx++;
			tinfo++;
		}
	}

	/* waiter info */
	idx    = 0;
	waiter = dctx->waiter_first;

	for (; idx<nwaiters; ) {
		if (waiter->msg.header.msg_id) {
			tinfo->tokpid  = waiter->client.tokpid;
			tinfo->tsyspid = waiter->client.cid.process_id;
			tinfo->pad     = 0;

			tinfo->keys[0] = waiter->msg.syncinfo.ipckeys[0];
			tinfo->keys[1] = waiter->msg.syncinfo.ipckeys[1];
			tinfo->keys[2] = waiter->msg.syncinfo.ipckeys[2];
			tinfo->keys[3] = waiter->msg.syncinfo.ipckeys[3];
			tinfo->keys[4] = waiter->msg.syncinfo.ipckeys[4];
			tinfo->keys[5] = waiter->msg.syncinfo.ipckeys[5];

			tinfo->meta[0] = waiter->msg.syncinfo.ipcsvc.keys.key[0];
			tinfo->meta[1] = waiter->msg.syncinfo.ipcsvc.keys.key[1];
			tinfo->meta[2] = waiter->msg.syncinfo.ipcsvc.keys.key[2];
			tinfo->meta[3] = waiter->msg.syncinfo.ipcsvc.keys.key[3];
			tinfo->meta[4] = waiter->msg.syncinfo.ipcsvc.keys.key[4];
			tinfo->meta[5] = waiter->msg.syncinfo.ipcsvc.keys.key[5];

			idx++;
			tinfo++;
		}

		if (++waiter == dctx->waiter_cap)
			waiter = dctx->waiter_base;
	}

	/* write */
	for (mark=addr; size; size-=iosb.info) {
		status = ntapi->zw_write_file(
			hfile,0,0,0,
			&iosb,addr,size,
			0,0);

		switch (status) {
			case NT_STATUS_SUCCESS:
				mark += iosb.info;
				addr  = mark;
				break;

			default:
				ntapi->zw_close(hfile);
				ntapi->zw_close(hprocess);

				if (tctx)
					toks_free(tctx);

				return status;
		}
	}

	/* buffer */
	if (tctx)
		toks_free(tctx);

	/* remote handle */
	status = ntapi->zw_duplicate_object(
		NT_CURRENT_PROCESS_HANDLE,
		hfile,
		hprocess,
		&hrfile,
		0,0,
		NT_DUPLICATE_SAME_ACCESS
			| NT_DUPLICATE_SAME_ATTRIBUTES);


	ntapi->zw_close(hfile);
	ntapi->zw_close(hprocess);

	switch (status) {
		case NT_STATUS_SUCCESS:
			dctx->reply.ttyinfo.exarg = hrfile;
			return NT_STATUS_SUCCESS;

		default:
			dctx->reply.ttyinfo.exarg = 0;
			return status;
	}
}

static int32_t toks_daemon_ioctl_log_token_info(struct toks_daemon_ctx * dctx)
{
	toks_log_token_info(dctx);
	return NT_STATUS_SUCCESS;
}

int32_t __stdcall toks_daemon_ioctl(struct toks_daemon_ctx * dctx)
{
	nt_tty_port_msg * msg;
	uint32_t          ctlcode;

	msg     = &dctx->reply;
	ctlcode = msg->ttyinfo.opdata;

	switch (ctlcode) {
		case TOKS_IOCTL_GET_TOKEN_COUNT:
			return toks_daemon_ioctl_get_token_count(dctx);

		case TOKS_IOCTL_SET_TOKEN_COUNT:
			return toks_daemon_ioctl_set_token_count(dctx);

		case TOKS_IOCTL_GET_SERVICE_INFO:
			return toks_daemon_ioctl_get_service_info(dctx);

		case TOKS_IOCTL_LOG_SERVICE_INFO:
			return toks_daemon_ioctl_log_service_info(dctx);

		case TOKS_IOCTL_GET_TOKEN_INFO:
			return toks_daemon_ioctl_get_token_info(dctx);

		case TOKS_IOCTL_LOG_TOKEN_INFO:
			return toks_daemon_ioctl_log_token_info(dctx);

		case TOKS_IOCTL_GET_LOG_LEVEL:
			return toks_daemon_ioctl_get_log_level(dctx);

		case TOKS_IOCTL_SET_LOG_LEVEL:
			return toks_daemon_ioctl_set_log_level(dctx);

		default:
			return NT_STATUS_NOT_IMPLEMENTED;
	}
}