Blob Blame History Raw
/********************************************************/
/*  ntapi: Native API core library                      */
/*  Copyright (C) 2013--2017  Z. Gilboa                 */
/*  Released under GPLv2 and GPLv3; see COPYING.NTAPI.  */
/********************************************************/

#include <psxtypes/psxtypes.h>
#include <ntapi/ntapi.h>
#include "ntapi_impl.h"

static int32_t __fastcall __tty_create_session_return(
	void *				hready,
	nt_create_process_params *	params,
	int32_t				status)
{
	if (params->hprocess && status)
		__ntapi->zw_terminate_process(
			params->hprocess,
			NT_STATUS_UNEXPECTED_IO_ERROR);

	__ntapi->zw_close(hready);
	__ntapi->zw_close(params->hprocess);
	__ntapi->zw_close(params->hthread);

	return status;
}

int32_t __stdcall __ntapi_tty_create_session(
	__out	void **			hport,
	__out	nt_port_name *		port_name,
	__in	nt_tty_session_type	type,
	__in	nt_tty_session_subtype	subtype,
	__in	const nt_guid *		guid		__optional,
	__in	wchar16_t *		image_name,
	__in	void *			htty		__optional)
{
	nt_status			status;
	ntapi_internals *		__internals;
	void *				shport;
	nt_port_name			sport_name;
	nt_tty_server_basic_info	ttyinfo;
	nt_iosb				iosb;

	nt_port_attr			port_attr;
	nt_runtime_data			ssattr;
	nt_runtime_data_block		rtblock;
	nt_create_process_params	params;
	nt_event_basic_information	eready;

	/* validate */
	if (!image_name)
		return NT_STATUS_INVALID_PARAMETER;

	/* init */
	__internals = __ntapi_internals();

	__ntapi->tt_aligned_block_memset(
		&port_attr,0,sizeof(port_attr));

	__ntapi->tt_aligned_block_memset(
		&ssattr,0,sizeof(ssattr));

	/* port type */
	if (guid) {
		if ((status = __ntapi->tt_port_type_from_guid(
				&port_attr.type,
				&port_attr.subtype,
				guid)))
			return status;
	} else {
		port_attr.type = NT_PORT_TYPE_SUBSYSTEM;
	}

	/* port subtype */
	switch (type) {
		case NT_TTY_SESSION_PRIMARY:
			port_attr.subtype = NT_PORT_SUBTYPE_DEFAULT;

			if (!hport)
				hport = &__internals->hport_tty_session;

			if (!port_name)
				port_name = __internals->subsystem;

			break;

		case NT_TTY_SESSION_SECONDARY:
			port_attr.subtype = NT_PORT_SUBTYPE_DEFAULT;

			if (!hport)
				hport = &shport;

			if (!port_name)
				port_name = &sport_name;

			break;

		case NT_TTY_SESSION_PRIVATE:
			port_attr.subtype = NT_PORT_SUBTYPE_PRIVATE;
			break;

		default:
			return NT_STATUS_INVALID_PARAMETER;
	}

	/* port guid */
	if (guid)
		__ntapi->tt_guid_copy(
			&port_attr.guid,
			guid);
	else
		__ntapi->tt_port_guid_from_type(
			&port_attr.guid,
			port_attr.type,
			port_attr.subtype);

	/* port keys */
	if ((status = __ntapi->tt_port_generate_keys(&port_attr.keys)))
		return status;

	/* port name */
	__ntapi->tt_port_name_from_attr(
		port_name,
		&port_attr);

	/* parent session) */
	if (htty && (htty != NT_INVALID_HANDLE_VALUE)) {
		if ((status = __ntapi->tty_query_information_server(
				htty,&iosb,
				&ttyinfo,sizeof(ttyinfo),
				NT_TTY_SERVER_BASIC_INFORMATION)))
			return status;

		ssattr.tty_type		= ttyinfo.attr.type;
		ssattr.tty_subtype	= ttyinfo.attr.subtype;

		ssattr.tty_keys[0]	= ttyinfo.attr.keys.key[0];
		ssattr.tty_keys[1]	= ttyinfo.attr.keys.key[1];
		ssattr.tty_keys[2]	= ttyinfo.attr.keys.key[2];
		ssattr.tty_keys[3]	= ttyinfo.attr.keys.key[3];
		ssattr.tty_keys[4]	= ttyinfo.attr.keys.key[4];
		ssattr.tty_keys[5]	= ttyinfo.attr.keys.key[5];

		__ntapi->tt_guid_copy(
			&ssattr.tty_guid,
			&ttyinfo.attr.guid);
	}

	/* subsystem attributes */
	__ntapi->tt_guid_copy(
		&ssattr.abi,
		&(nt_guid)NT_PROCESS_GUID_RTDATA);

	ssattr.port_type	= port_attr.type;
	ssattr.port_subtype	= port_attr.subtype;

	__ntapi->tt_guid_copy(
		&ssattr.port_guid,
		&port_attr.guid);

	ssattr.srv_type		= type;
	ssattr.srv_subtype	= subtype;

	ssattr.srv_keys[0]	= port_attr.keys.key[0];
	ssattr.srv_keys[1]	= port_attr.keys.key[1];
	ssattr.srv_keys[2]	= port_attr.keys.key[2];
	ssattr.srv_keys[3]	= port_attr.keys.key[3];
	ssattr.srv_keys[4]	= port_attr.keys.key[4];
	ssattr.srv_keys[5]	= port_attr.keys.key[5];

	__ntapi->tt_guid_copy(
		&ssattr.srv_guid,
		&port_attr.guid);

	if ((status = __ntapi->tt_create_private_event(
			&ssattr.hserver,
			NT_NOTIFICATION_EVENT,
			NT_EVENT_NOT_SIGNALED)))
		return status;

	/* create subsystem process */
	rtblock.addr		= &ssattr;
	rtblock.size		= sizeof(ssattr);
	rtblock.remote_addr	= 0;
	rtblock.remote_size	= 0;
	rtblock.flags		= NT_RUNTIME_DATA_DUPLICATE_SESSION_HANDLES;

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

	params.image_name	= image_name;
	params.rtblock		= &rtblock;
	params.hsession		= htty;

	if ((status = __ntapi->tt_create_native_process(&params)))
		return __tty_create_session_return(ssattr.hserver,&params,status);

	__ntapi->zw_wait_for_multiple_objects(
		2,
		(void *[]){ssattr.hserver,params.hprocess},
		NT_WAIT_ANY,
		NT_SYNC_NON_ALERTABLE,
		0);

	if ((status = __ntapi->zw_query_event(
			ssattr.hserver,
			NT_EVENT_BASIC_INFORMATION,
			&eready,
			sizeof(eready),
			&(size_t){0})))
		return __tty_create_session_return(ssattr.hserver,&params,status);

	if (!eready.signal_state)
		return __tty_create_session_return(ssattr.hserver,&params,NT_STATUS_SYSTEM_PROCESS_TERMINATED);

	/* connect to subsystem */
	if ((status = __ntapi->tty_connect(
			hport,
			&port_name->base_named_objects[0],
			NT_SECURITY_IMPERSONATION)))
		return __tty_create_session_return(ssattr.hserver,&params,status);

	/* finalize primary session */
	if (type == NT_TTY_SESSION_PRIMARY) {
		if (hport != &__internals->hport_tty_session)
			__internals->hport_tty_session = *hport;

		if (port_name != __internals->subsystem)
			__ntapi->tt_memcpy_utf16(
				__internals->subsystem->base_named_objects,
				port_name->base_named_objects,
				sizeof(*port_name));
	};

	return __tty_create_session_return(ssattr.hserver,&params,NT_STATUS_SUCCESS);
}