Esempio n. 1
0
/*
 * Return a matching hv_vmbus_device_id pointer.
 * If there is no match, return NULL.
 */
static const struct hv_vmbus_device_id *hv_vmbus_get_id(
					const struct hv_vmbus_device_id *id,
					const __u8 *guid)
{
	for (; !is_null_guid(id->guid); id++)
		if (!memcmp(&id->guid, guid, sizeof(uuid_le)))
			return id;

	return NULL;
}
/*!
 * @brief Receive a new packet on the given remote endpoint.
 * @param remote Pointer to the \c Remote instance.
 * @param packet Pointer to a pointer that will receive the \c Packet data.
 * @return An indication of the result of processing the transmission request.
 */
static DWORD packet_receive_named_pipe(Remote *remote, Packet **packet)
{
	DWORD headerBytes = 0, payloadBytesLeft = 0, res;
	PacketHeader header = { 0 };
	LONG bytesRead;
	BOOL inHeader = TRUE;
	PUCHAR packetBuffer = NULL;
	PUCHAR payload = NULL;
	ULONG payloadLength;
	NamedPipeTransportContext* ctx = (NamedPipeTransportContext*)remote->transport->ctx;

	lock_acquire(remote->lock);

	dprintf("[PIPE PACKET RECEIVE] reading in the header from pipe handle: %p", ctx->pipe);
	// Read the packet length
	while (inHeader)
	{
		if (!ReadFile(ctx->pipe, ((PUCHAR)&header + headerBytes), sizeof(PacketHeader)-headerBytes, &bytesRead, NULL))
		{
			SetLastError(ERROR_NOT_FOUND);
			goto out;
		}

		headerBytes += bytesRead;

		if (headerBytes != sizeof(PacketHeader))
		{
			vdprintf("[PIPE] More bytes required");
			continue;
		}

		inHeader = FALSE;
	}

	if (headerBytes != sizeof(PacketHeader))
	{
		dprintf("[PIPE] we didn't get enough header bytes");
		goto out;
	}

	vdprintf("[PIPE] the XOR key is: %02x%02x%02x%02x", header.xor_key[0], header.xor_key[1], header.xor_key[2], header.xor_key[3]);

#ifdef DEBUGTRACE
	PUCHAR h = (PUCHAR)&header;
	dprintf("[PIPE] Packet header: [0x%02X 0x%02X 0x%02X 0x%02X] [0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X] [0x%02X 0x%02X 0x%02X 0x%02X] [0x%02X 0x%02X 0x%02X 0x%02X] [0x%02X 0x%02X 0x%02X 0x%02X]",
		h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7], h[8], h[9], h[10], h[11], h[12], h[13], h[14], h[15], h[16], h[17], h[18], h[19], h[20], h[21], h[22], h[23], h[24], h[25], h[26], h[27], h[28], h[29], h[30], h[31]);
#endif

	// At this point, we might have read in a valid TLV packet, or we might have read in the first chunk of data
	// from a staged listener after a reconnect. We can figure this out rather lazily by assuming the following:
	// XOR keys are always 4 bytes that are non-zero. If the higher order byte of the xor key is zero, then it
	// isn't an XOR Key, instead it's the 4-byte length of the metsrv binary (because metsrv isn't THAT big).
	if (header.xor_key[3] == 0)
	{
		// looks like we have a metsrv instance, time to ignore it.
		int length = *(int*)&header.xor_key[0];
		dprintf("[PIPE] discovered a length header, assuming it's metsrv of length %d", length);

		int bytesToRead = length - sizeof(PacketHeader) + sizeof(DWORD);
		char* buffer = (char*)malloc(bytesToRead);
		read_raw_bytes_to_buffer(ctx, buffer, bytesToRead, &bytesRead);
		free(buffer);

		// did something go wrong.
		if (bytesToRead != bytesRead)
		{
			dprintf("[PIPE] Failed to read all bytes when flushing the buffer: %u vs %u", bytesToRead, bytesRead);
			goto out;
		}

		// indicate success, but don't return a packet for processing
		SetLastError(ERROR_SUCCESS);
		*packet = NULL;
	}
	else
	{
		vdprintf("[PIPE] XOR key looks fine, moving on");
		// xor the header data
		xor_bytes(header.xor_key, (PUCHAR)&header + sizeof(header.xor_key), sizeof(PacketHeader) - sizeof(header.xor_key));
#ifdef DEBUGTRACE
		PUCHAR h = (PUCHAR)&header;
		dprintf("[PIPE] Packet header: [0x%02X 0x%02X 0x%02X 0x%02X] [0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X] [0x%02X 0x%02X 0x%02X 0x%02X] [0x%02X 0x%02X 0x%02X 0x%02X] [0x%02X 0x%02X 0x%02X 0x%02X]",
			h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7], h[8], h[9], h[10], h[11], h[12], h[13], h[14], h[15], h[16], h[17], h[18], h[19], h[20], h[21], h[22], h[23], h[24], h[25], h[26], h[27], h[28], h[29], h[30], h[31]);
#endif

		// if we don't have a GUID yet, we need to take the one given in the packet
		if (is_null_guid(remote->orig_config->session.session_guid))
		{
			memcpy(remote->orig_config->session.session_guid, header.session_guid, sizeof(remote->orig_config->session.session_guid));
		}

		payloadLength = ntohl(header.length) - sizeof(TlvHeader);
		dprintf("[PIPE] Payload length is %u 0x%08x", payloadLength, payloadLength);
		DWORD packetSize = sizeof(PacketHeader) + payloadLength;
		dprintf("[PIPE] total buffer size for the packet is %u 0x%08x", packetSize, packetSize);
		payloadBytesLeft = payloadLength;

		// Allocate the payload
		if (!(packetBuffer = (PUCHAR)malloc(packetSize)))
		{
			dprintf("[PIPE] Failed to create the packet buffer");
			SetLastError(ERROR_NOT_ENOUGH_MEMORY);
			goto out;
		}
		dprintf("[PIPE] Allocated packet buffer at %p", packetBuffer);

		// we're done with the header data, so we need to re-encode it, as the packet decryptor is going to
		// handle the extraction for us.
		xor_bytes(header.xor_key, (LPBYTE)&header.session_guid[0], sizeof(PacketHeader) - sizeof(header.xor_key));
		// Copy the packet header stuff over to the packet
		memcpy_s(packetBuffer, sizeof(PacketHeader), (LPBYTE)&header, sizeof(PacketHeader));

		payload = packetBuffer + sizeof(PacketHeader);

		// Read the payload
		res = read_raw_bytes_to_buffer(ctx, payload, payloadLength, &bytesRead);
		dprintf("[PIPE] wanted %u read %u", payloadLength, bytesRead);

		// Didn't finish?
		if (bytesRead != payloadLength)
		{
			dprintf("[PIPE] Failed to get all the payload bytes");
			SetLastError(res);
			goto out;
		}

		vdprintf("[PIPE] decrypting packet");
		SetLastError(decrypt_packet(remote, packet, packetBuffer, packetSize));

		free(packetBuffer);
		packetBuffer = NULL;
	}
out:
	res = GetLastError();

	// Cleanup
	if (packetBuffer)
	{
		free(packetBuffer);
	}

	lock_release(remote->lock);

	return res;
}
/*!
 * @brief Windows-specific function to receive a new packet via one of the HTTP libs (WinInet or WinHTTP).
 * @param remote Pointer to the \c Remote instance.
 * @param packet Pointer to a pointer that will receive the \c Packet data.
 * @return An indication of the result of processing the transmission request.
 */
static DWORD packet_receive_http(Remote *remote, Packet **packet)
{
	DWORD headerBytes = 0, payloadBytesLeft = 0, res;
	Packet *localPacket = NULL;
	PacketHeader header;
	LONG bytesRead;
	BOOL inHeader = TRUE;
	PUCHAR packetBuffer = NULL;
	ULONG payloadLength;
	HttpTransportContext* ctx = (HttpTransportContext*)remote->transport->ctx;

	HINTERNET hReq;
	BOOL hRes;
	DWORD retries = 5;

	lock_acquire(remote->lock);

	hReq = ctx->create_req(ctx, TRUE, "PACKET RECEIVE");
	if (hReq == NULL)
	{
		goto out;
	}

	vdprintf("[PACKET RECEIVE HTTP] sending GET");
	hRes = ctx->send_req(ctx, hReq, NULL, 0);

	if (!hRes)
	{
		dprintf("[PACKET RECEIVE HTTP] Failed send_req: %d %d", GetLastError(), WSAGetLastError());
		SetLastError(ERROR_NOT_FOUND);
		goto out;
	}

	vdprintf("[PACKET RECEIVE HTTP] Waiting to see the response ...");
	if (ctx->receive_response && !ctx->receive_response(hReq))
	{
		vdprintf("[PACKET RECEIVE] Failed receive: %d", GetLastError());
		SetLastError(ERROR_NOT_FOUND);
		goto out;
	}

	SetLastError(ctx->validate_response(hReq, ctx));

	if (GetLastError() != ERROR_SUCCESS)
	{
		goto out;
	}

	// Read the packet length
	retries = 3;
	vdprintf("[PACKET RECEIVE HTTP] Start looping through the receive calls");
	while (inHeader && retries > 0)
	{
		retries--;
		if (!ctx->read_response(hReq, (PUCHAR)&header + headerBytes, sizeof(PacketHeader)-headerBytes, &bytesRead))
		{
			dprintf("[PACKET RECEIVE HTTP] Failed HEADER read_response: %d", GetLastError());
			SetLastError(ERROR_NOT_FOUND);
			goto out;
		}

		vdprintf("[PACKET RECEIVE NHTTP] Data received: %u bytes", bytesRead);

		// If the response contains no data, this is fine, it just means the
		// remote side had nothing to tell us. Indicate this through a
		// ERROR_EMPTY response code so we can update the timestamp.
		if (bytesRead == 0)
		{
			SetLastError(ERROR_EMPTY);
			goto out;
		}

		headerBytes += bytesRead;

		if (headerBytes != sizeof(PacketHeader))
		{
			continue;
		}

		inHeader = FALSE;
	}

	if (headerBytes != sizeof(PacketHeader))
	{
		dprintf("[PACKET RECEIVE HTTP] headerBytes not valid");
		SetLastError(ERROR_NOT_FOUND);
		goto out;
	}

	dprintf("[PACKET RECEIVE HTTP] decoding header");
	PacketHeader encodedHeader;
	memcpy(&encodedHeader, &header, sizeof(PacketHeader));
	xor_bytes(header.xor_key, (PUCHAR)&header + sizeof(header.xor_key), sizeof(PacketHeader) - sizeof(header.xor_key));

#ifdef DEBUGTRACE
	PUCHAR h = (PUCHAR)&header;
	vdprintf("[HTTP] Packet header: [0x%02X 0x%02X 0x%02X 0x%02X] [0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X] [0x%02X 0x%02X 0x%02X 0x%02X] [0x%02X 0x%02X 0x%02X 0x%02X] [0x%02X 0x%02X 0x%02X 0x%02X]",
		   h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7], h[8], h[9], h[10], h[11], h[12], h[13], h[14], h[15], h[16], h[17], h[18], h[19], h[20], h[21], h[22], h[23], h[24], h[25], h[26], h[27], h[28], h[29], h[30], h[31]);
#endif

	payloadLength = ntohl(header.length) - sizeof(TlvHeader);
	vdprintf("[REC HTTP] Payload length is %d", payloadLength);
	DWORD packetSize = sizeof(PacketHeader) + payloadLength;
	vdprintf("[REC HTTP] total buffer size for the packet is %d", packetSize);
	payloadBytesLeft = payloadLength;

	// Allocate the payload
	if (!(packetBuffer = (PUCHAR)malloc(packetSize)))
	{
		dprintf("[REC HTTP] Failed to create the packet buffer");
		SetLastError(ERROR_NOT_ENOUGH_MEMORY);
		goto out;
	}
	dprintf("[REC HTTP] Allocated packet buffer at %p", packetBuffer);

	// Copy the packet header stuff over to the packet
	memcpy_s(packetBuffer, sizeof(PacketHeader), (LPBYTE)&encodedHeader, sizeof(PacketHeader));

	LPBYTE payload = packetBuffer + sizeof(PacketHeader);

	// Read the payload
	retries = payloadBytesLeft;
	while (payloadBytesLeft > 0 && retries > 0)
	{
		vdprintf("[PACKET RECEIVE HTTP] reading more data from the body...");
		retries--;
		if (!ctx->read_response(hReq, payload + payloadLength - payloadBytesLeft, payloadBytesLeft, &bytesRead))
		{
			dprintf("[PACKET RECEIVE] Failed BODY read_response: %d", GetLastError());
			SetLastError(ERROR_NOT_FOUND);
			goto out;
		}

		if (!bytesRead)
		{
			vdprintf("[PACKET RECEIVE HTTP] no bytes read, bailing out");
			SetLastError(ERROR_NOT_FOUND);
			goto out;
		}

		vdprintf("[PACKET RECEIVE HTTP] bytes read: %u", bytesRead);
		payloadBytesLeft -= bytesRead;
	}

	// Didn't finish?
	if (payloadBytesLeft)
	{
		goto out;
	}

#ifdef DEBUGTRACE
	h = (PUCHAR)&header.session_guid[0];
	dprintf("[HTTP] Packet Session GUID: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
		h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7], h[8], h[9], h[10], h[11], h[12], h[13], h[14], h[15]);
#endif

	if (is_null_guid(header.session_guid) || memcmp(remote->orig_config->session.session_guid, header.session_guid, sizeof(header.session_guid)) == 0)
	{
		dprintf("[HTTP] Session GUIDs match (or packet guid is null), decrypting packet");
		SetLastError(decrypt_packet(remote, packet, packetBuffer, packetSize));
	}
	else
	{
		dprintf("[HTTP] Session GUIDs don't match, looking for a pivot");
		PivotContext* pivotCtx = pivot_tree_find(remote->pivot_sessions, header.session_guid);
		if (pivotCtx != NULL)
		{
			dprintf("[HTTP] Pivot found, dispatching packet on a thread (to avoid main thread blocking)");
			SetLastError(pivot_packet_dispatch(pivotCtx, packetBuffer, packetSize));

			// mark this packet buffer as NULL as the thread will clean it up
			packetBuffer = NULL;
			*packet = NULL;
		}
		else
		{
			dprintf("[HTTP] Session GUIDs don't match, can't find pivot!");
		}
	}

out:
	res = GetLastError();

	dprintf("[HTTP] Cleaning up");
	SAFE_FREE(packetBuffer);

	// Cleanup on failure
	if (res != ERROR_SUCCESS)
	{
		SAFE_FREE(localPacket);
	}

	if (hReq)
	{
		ctx->close_req(hReq);
	}

	lock_release(remote->lock);

	dprintf("[HTTP] Packet receive finished");

	return res;
}