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 <toksvc/toksvc.h>
#include "toksvc_daemon_impl.h"
#include "toksvc_driver_impl.h"

static void toks_daemon_token_release(struct toks_token * token)
{
	void *			hport;
	struct _nt_tty_sync_msg	msg;
	uint32_t *		keys;

	hport = token->client.hdaemon;
	keys  = token->keys.key;

	ntapi->tt_aligned_block_memset(
		&msg,0,sizeof(msg));

	msg.header.msg_type	= NT_LPC_NEW_MESSAGE;
	msg.header.data_size	= sizeof(msg.data);
	msg.header.msg_size	= sizeof(msg);
	msg.data.ttyinfo.opcode	= TOKS_DAEMON_RELEASE;

	msg.data.syncinfo.ipckeys[0] = keys[0];
	msg.data.syncinfo.ipckeys[1] = keys[1];
	msg.data.syncinfo.ipckeys[2] = keys[2];
	msg.data.syncinfo.ipckeys[3] = keys[3];
	msg.data.syncinfo.ipckeys[4] = keys[4];
	msg.data.syncinfo.ipckeys[5] = keys[5];

	ntapi->zw_request_wait_reply_port(
		hport,&msg,&msg);
}

static int32_t toks_daemon_client_wait(void * rapunzel)
{

	int32_t                  status;
	nt_ebi                   syncebi;
	struct toks_token *      token;
	struct toks_client_ctx * client;
	void *                   hduo[2];

	token  = (struct toks_token *)rapunzel;
	client = &token->client;

	ntapi->zw_set_event(client->hswap,0);

	hduo[0] = client->hprocess;
	hduo[1] = client->halert;

	ntapi->zw_wait_for_multiple_objects(
		2,hduo,
		NT_WAIT_ANY,
		NT_SYNC_NON_ALERTABLE,
		0);

	status = ntapi->zw_query_event(
		client->halert,
		NT_EVENT_BASIC_INFORMATION,
		&syncebi,sizeof(syncebi),
		&(size_t){0});

	if ((status == NT_STATUS_SUCCESS) && !syncebi.signal_state)
		toks_daemon_token_release(token);

	ntapi->zw_close(client->hprocess);
	ntapi->zw_close(client->halert);

	return ntapi->zw_terminate_thread(
		NT_CURRENT_THREAD_HANDLE,
		NT_STATUS_SUCCESS);
}

static void toks_daemon_token_reset(struct toks_token * token)
{
	token->self        = 0;
	token->keys.key[0] = 0;
	token->keys.key[1] = 0;
	token->keys.key[2] = 0;
	token->keys.key[3] = 0;
	token->keys.key[4] = 0;
	token->keys.key[5] = 0;
}

static int32_t toks_daemon_token_instance(
	struct toks_token *         token,
	struct toks_client_ctx *    client)
{
	int32_t			status;
	nt_thread_params	params;

	if ((status = ntapi->tt_create_private_event(
			&client->hswap,
			NT_NOTIFICATION_EVENT,
			NT_EVENT_NOT_SIGNALED)))
		return status;

	status = ntapi->tt_create_private_event(
		&client->halert,
		NT_NOTIFICATION_EVENT,
		NT_EVENT_NOT_SIGNALED);

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

	token->self                     = token;
	token->client.hprocess          = client->hprocess;
	token->client.hswap             = client->hswap;
	token->client.halert            = client->halert;
	token->client.hdaemon           = client->hdaemon;
	token->client.cid.process_id    = client->cid.process_id;
	token->client.cid.thread_id     = client->cid.thread_id;
	token->client.tokpid            = client->tokpid;

	ntapi->tt_aligned_block_memset(
		&params,0,sizeof(params));

	params.hprocess		  = NT_CURRENT_PROCESS_HANDLE;
	params.start		  = toks_daemon_client_wait;
	params.ext_ctx            = token;
	params.ext_ctx_size       = sizeof(*token);
	params.stack_size_commit  = 4 * 1024;
	params.stack_size_reserve = 4 * 1024;
	params.creation_flags	  = NT_CREATE_LOCAL_THREAD;

	if ((status = ntapi->tt_create_thread(&params))) {
		toks_daemon_token_reset(token);
		ntapi->zw_close(client->hswap);
		ntapi->zw_close(client->halert);
		return status;
	}

	status = ntapi->zw_wait_for_single_object(
		client->hswap,
		NT_SYNC_NON_ALERTABLE,
		0);

	ntapi->zw_close(client->hswap);

	if (status) {
		toks_daemon_token_reset(token);
		ntapi->zw_close(client->halert);
		return status;
	}

	token->client.hinstance = params.hthread;

	return NT_STATUS_SUCCESS;
}

static int32_t toks_daemon_queue(struct toks_daemon_ctx * dctx, const struct toks_client_ctx * client)
{
	int                         nwaiters;
	struct toks_waiter *        waiter;
	nt_tty_port_msg *           msg;
	void *                      hevent;
	int32_t                     status;

	msg      = &dctx->reply;
	nwaiters = toks_get_driver_nwaiters(dctx->driver_ctx);

	if (nwaiters == TOKS_MAX_WAITERS)
		return NT_STATUS_TIMEOUT;


	status = ntapi->zw_duplicate_object(
		client->hcaller,
		msg->syncinfo.hevent,
		NT_CURRENT_PROCESS_HANDLE,
		&hevent,
		NT_EVENT_QUERY_STATE|NT_EVENT_MODIFY_STATE,
		0,0);

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

	if ((status = ntapi->zw_reset_event(hevent,&(int){0}))) {
		ntapi->zw_close(client->hprocess);
		ntapi->zw_close(hevent);
		return status;
	}


	waiter = dctx->waiter_next;

	waiter->client.hprocess = client->hprocess;
	waiter->client.hcaller  = client->hcaller;
	waiter->client.hevent   = hevent;

	waiter->client.tokpid         = client->tokpid;
	waiter->client.cid.process_id = client->cid.process_id;

	toks_set_driver_nwaiters(
		dctx->driver_ctx,
		++nwaiters);

	dctx->waiter_next++;

	if (dctx->waiter_next == dctx->waiter_cap)
		dctx->waiter_next = dctx->waiter_base;

	ntapi->tt_generic_memcpy(
		&waiter->msg,msg,
		sizeof(*msg));

	return NT_STATUS_PENDING;
}

int32_t __stdcall toks_daemon_acquire(struct toks_daemon_ctx * dctx)
{
	int32_t                     status;
	nt_tty_port_msg *           msg;
	struct toks_client_ctx      client;
	nt_oa                       oa;
	struct toks_token *         token;
	struct toks_token *         tocap;

	msg                   = &dctx->reply;

	client.hport          = 0;
	client.hprocess       = 0;
	client.hcaller        = 0;
	client.hswap          = 0;
	client.hdaemon        = dctx->hport_internal_client;
	client.cid.process_id = msg->header.client_id.process_id;
	client.cid.thread_id  = 0;
	client.tokpid         = 0;

	oa.len      = sizeof(oa);
	oa.root_dir = 0;
	oa.obj_name = 0;
	oa.obj_attr = 0;
	oa.sec_desc = 0;
	oa.sec_qos  = 0;

	if (dctx->opcode == TOKS_DAEMON_ACQUIRE) {
		if ((dctx->reqtokpid = msg->syncinfo.ipcsvc.keys.reserved)) {
			if (msg->syncinfo.ipcsvc.options & TOKS_OPT_FRAMEWORK_PID) {
				if ((status = toks_daemon_pidopen(dctx)))
					return status;
			} else {
				dctx->reqsyspid = dctx->reqtokpid;
			}

			client.tokpid         = dctx->reqtokpid;
			client.cid.process_id = dctx->reqsyspid;
		}

		if ((status = ntapi->zw_open_process(
				&client.hprocess,
				NT_PROCESS_SYNCHRONIZE
					| NT_PROCESS_QUERY_INFORMATION,
				&oa,&client.cid)))
			return status;
	}

	if ((dctx->ftokens == 0)  && !msg->syncinfo.hevent) {
		ntapi->zw_close(client.hprocess);
		return NT_STATUS_TIMEOUT;
	}

	toks_query_performance_counters(dctx->driver_ctx,&dctx->pcnt);

	if (dctx->ftokens == 0) {
		status = ntapi->zw_open_process(
			&client.hcaller,
			NT_PROCESS_SYNCHRONIZE
				| NT_PROCESS_DUP_HANDLE
				| NT_PROCESS_QUERY_INFORMATION,
			&oa,&msg->header.client_id);

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

		msg->syncinfo.ipckeys[0] = ntapi->tt_buffer_crc32(
			msg->header.msg_id,
			&dctx->pcnt,sizeof(dctx->pcnt));

		msg->syncinfo.ipckeys[1] = ntapi->tt_buffer_crc32(
			msg->header.msg_id,
			&msg->header,sizeof(msg->header));

		msg->syncinfo.ipckeys[2] = ntapi->tt_buffer_crc32(
			msg->header.msg_id,
			&msg->syncinfo,sizeof(msg->syncinfo));

		msg->syncinfo.ipckeys[3] = ntapi->tt_buffer_crc32(
			msg->header.msg_id,
			&client,sizeof(client));

		msg->syncinfo.ipckeys[4] = ntapi->tt_buffer_crc32(
			msg->header.msg_id,
			toks_get_driver_tokens(dctx->driver_ctx),
			toks_get_driver_ntokens(dctx->driver_ctx)
			* sizeof(struct toks_token));

		msg->syncinfo.ipckeys[5] = ntapi->tt_buffer_crc32(
			msg->header.msg_id,
			dctx,sizeof(*dctx));

		return toks_daemon_queue(dctx,&client);
	}

	token = toks_get_driver_tokens(dctx->driver_ctx);
	tocap = &token[toks_get_driver_atokens(dctx->driver_ctx)];

	for (; token->self && (token<tocap); )
		token++;

	if (dctx->opcode == TOKS_DAEMON_RELEASE) {
		client.hprocess    = msg->ttyinfo.exarg;

		token->keys.key[0] = msg->syncinfo.ipckeys[0];
		token->keys.key[1] = msg->syncinfo.ipckeys[1];
		token->keys.key[2] = msg->syncinfo.ipckeys[2];
		token->keys.key[3] = msg->syncinfo.ipckeys[3];
		token->keys.key[4] = msg->syncinfo.ipckeys[4];
		token->keys.key[5] = msg->syncinfo.ipckeys[5];
	} else {
		token->keys.key[0] = ntapi->tt_buffer_crc32(
			msg->header.msg_id,
			&dctx->pcnt,sizeof(dctx->pcnt));

		token->keys.key[1] = ntapi->tt_buffer_crc32(
			msg->header.msg_id,
			&msg->header,sizeof(msg->header));

		token->keys.key[2] = ntapi->tt_buffer_crc32(
			msg->header.msg_id,
			&msg->syncinfo,sizeof(msg->syncinfo));

		token->keys.key[3] = ntapi->tt_buffer_crc32(
			msg->header.msg_id,
			&client,sizeof(client));

		token->keys.key[4] = ntapi->tt_buffer_crc32(
			msg->header.msg_id,
			toks_get_driver_tokens(dctx->driver_ctx),
			toks_get_driver_ntokens(dctx->driver_ctx)
			* sizeof(struct toks_token));

		token->keys.key[5] = ntapi->tt_buffer_crc32(
			msg->header.msg_id,
			&token->keys,sizeof(token->keys));

		msg->syncinfo.ipckeys[0] = token->keys.key[0];
		msg->syncinfo.ipckeys[1] = token->keys.key[1];
		msg->syncinfo.ipckeys[2] = token->keys.key[2];
		msg->syncinfo.ipckeys[3] = token->keys.key[3];
		msg->syncinfo.ipckeys[4] = token->keys.key[4];
		msg->syncinfo.ipckeys[5] = token->keys.key[5];
	}

	if ((token->meta.key[0] = msg->syncinfo.ipcsvc.keys.key[0])) {
		token->meta.key[1] = msg->syncinfo.ipcsvc.keys.key[1];
		token->meta.key[2] = msg->syncinfo.ipcsvc.keys.key[2];
		token->meta.key[3] = msg->syncinfo.ipcsvc.keys.key[3];
		token->meta.key[4] = msg->syncinfo.ipcsvc.keys.key[4];
		token->meta.key[5] = msg->syncinfo.ipcsvc.keys.key[5];
	}

	if ((status = toks_daemon_token_instance(token,&client))) {
		toks_daemon_token_reset(token);
		ntapi->zw_close(client.hprocess);
		return status;
	}

	dctx->utokens++;
	dctx->ftokens--;

	return NT_STATUS_SUCCESS;
}