libfreerdp-core: remove usage of magic 60-byte pad in TsProxyCreateTunnelWriteRequest

This commit is contained in:
Marc-André Moreau 2015-02-01 15:58:32 -05:00
parent 9406ac188c
commit 4f173ae52a
5 changed files with 194 additions and 141 deletions

View File

@ -26,8 +26,6 @@
#include <winpr/stream.h>
#include <winpr/string.h>
#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#endif

View File

@ -21,11 +21,8 @@
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winpr/crt.h>
#include <freerdp/log.h>
#include "rpc_client.h"
@ -51,8 +48,6 @@ const p_uuid_t TSGU_UUID =
{ 0x3C, 0xDB, 0x6E, 0x7A, 0x27, 0x29 } /* node[6] */
};
#define TSGU_SYNTAX_IF_VERSION 0x00030001
const p_uuid_t NDR_UUID =
{
0x8A885D04, /* time_low */
@ -63,8 +58,6 @@ const p_uuid_t NDR_UUID =
{ 0x08, 0x00, 0x2B, 0x10, 0x48, 0x60 } /* node[6] */
};
#define NDR_SYNTAX_IF_VERSION 0x00000002
const p_uuid_t BTFN_UUID =
{
0x6CB71C2C, /* time_low */
@ -75,8 +68,6 @@ const p_uuid_t BTFN_UUID =
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } /* node[6] */
};
#define BTFN_SYNTAX_IF_VERSION 0x00000001
/**
* SECURE_BIND: RPC bind PDU with sec_trailer and auth_token. Auth_token is generated by calling
* the implementation equivalent of the abstract GSS_Init_sec_context call. Upon receiving that, the
@ -149,12 +140,11 @@ int rpc_send_bind_pdu(rdpRpc* rpc)
}
if (!ntlm_client_init(rpc->ntlm, FALSE, settings->GatewayUsername, settings->GatewayDomain, settings->GatewayPassword, NULL) ||
!ntlm_client_make_spn(rpc->ntlm, NULL, settings->GatewayHostname) ||
!ntlm_authenticate(rpc->ntlm)
)
!ntlm_client_make_spn(rpc->ntlm, NULL, settings->GatewayHostname) || !ntlm_authenticate(rpc->ntlm))
return -1;
bind_pdu = (rpcconn_bind_hdr_t*) calloc(1, sizeof(rpcconn_bind_hdr_t));
if (!bind_pdu)
return -1;
@ -175,7 +165,8 @@ int rpc_send_bind_pdu(rdpRpc* rpc)
bind_pdu->p_context_elem.reserved = 0;
bind_pdu->p_context_elem.reserved2 = 0;
bind_pdu->p_context_elem.p_cont_elem = malloc(sizeof(p_cont_elem_t) * bind_pdu->p_context_elem.n_context_elem);
bind_pdu->p_context_elem.p_cont_elem = calloc(bind_pdu->p_context_elem.n_context_elem, sizeof(p_cont_elem_t));
if (!bind_pdu->p_context_elem.p_cont_elem)
return -1;
@ -188,6 +179,10 @@ int rpc_send_bind_pdu(rdpRpc* rpc)
p_cont_elem->abstract_syntax.if_version = TSGU_SYNTAX_IF_VERSION;
p_cont_elem->transfer_syntaxes = malloc(sizeof(p_syntax_id_t));
if (!p_cont_elem->transfer_syntaxes)
return -1;
CopyMemory(&(p_cont_elem->transfer_syntaxes[0].if_uuid), &NDR_UUID, sizeof(p_uuid_t));
p_cont_elem->transfer_syntaxes[0].if_version = NDR_SYNTAX_IF_VERSION;
@ -200,6 +195,10 @@ int rpc_send_bind_pdu(rdpRpc* rpc)
p_cont_elem->abstract_syntax.if_version = TSGU_SYNTAX_IF_VERSION;
p_cont_elem->transfer_syntaxes = malloc(sizeof(p_syntax_id_t));
if (!p_cont_elem->transfer_syntaxes)
return -1;
CopyMemory(&(p_cont_elem->transfer_syntaxes[0].if_uuid), &BTFN_UUID, sizeof(p_uuid_t));
p_cont_elem->transfer_syntaxes[0].if_version = BTFN_SYNTAX_IF_VERSION;
@ -235,6 +234,7 @@ int rpc_send_bind_pdu(rdpRpc* rpc)
length = bind_pdu->frag_length;
clientCall = rpc_client_call_new(bind_pdu->call_id, 0);
if (!clientCall)
{
free(buffer);
@ -293,6 +293,9 @@ int rpc_recv_bind_ack_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
rpc->ntlm->inputBuffer[0].cbBuffer = header->common.auth_length;
rpc->ntlm->inputBuffer[0].pvBuffer = malloc(header->common.auth_length);
if (!rpc->ntlm->inputBuffer[0].pvBuffer)
return -1;
auth_data = buffer + (header->common.frag_length - header->common.auth_length);
CopyMemory(rpc->ntlm->inputBuffer[0].pvBuffer, auth_data, header->common.auth_length);

View File

@ -24,6 +24,15 @@
#include <winpr/wtypes.h>
const p_uuid_t TSGU_UUID;
#define TSGU_SYNTAX_IF_VERSION 0x00030001
const p_uuid_t NDR_UUID;
#define NDR_SYNTAX_IF_VERSION 0x00000002
const p_uuid_t BTFN_UUID;
#define BTFN_SYNTAX_IF_VERSION 0x00000001
int rpc_send_bind_pdu(rdpRpc* rpc);
int rpc_recv_bind_ack_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length);
int rpc_send_rpc_auth_3_pdu(rdpRpc* rpc);

View File

@ -121,149 +121,162 @@ int rpc_client_receive_pipe_read(rdpRpc* rpc, BYTE* buffer, size_t length)
int rpc_client_on_pdu_received_event(rdpRpc* rpc, RPC_PDU* pdu)
{
RpcClientCall* call;
if (pdu->Type != PTYPE_RESPONSE)
{
Queue_Enqueue(rpc->client->ReceiveQueue, pdu);
return 1;
}
else
{
call = rpc_client_call_find_by_id(rpc, pdu->CallId);
if (call->OpNum != TsProxySetupReceivePipeOpnum)
{
Queue_Enqueue(rpc->client->ReceiveQueue, pdu);
}
else
{
rpc_client_receive_pipe_write(rpc, Stream_Buffer(pdu->s), Stream_Length(pdu->s));
rpc_client_receive_pool_return(rpc, pdu);
}
}
Queue_Enqueue(rpc->client->ReceiveQueue, pdu);
return 1;
}
int rpc_client_on_fragment_received_event(rdpRpc* rpc, wStream* fragment)
{
BYTE* buffer;
RPC_PDU* pdu;
UINT32 StubOffset;
UINT32 StubLength;
RpcClientCall* call;
rpcconn_hdr_t* header;
if (!rpc->client->pdu)
rpc->client->pdu = rpc_client_receive_pool_take(rpc);
buffer = (BYTE*) Stream_Buffer(fragment);
header = (rpcconn_hdr_t*) Stream_Buffer(fragment);
if (rpc->State < RPC_CLIENT_STATE_CONTEXT_NEGOTIATED)
if (header->common.ptype == PTYPE_RESPONSE)
{
rpc->client->pdu->Flags = 0;
rpc->client->pdu->Type = header->common.ptype;
rpc->client->pdu->CallId = header->common.call_id;
Stream_EnsureCapacity(rpc->client->pdu->s, Stream_Length(fragment));
Stream_Write(rpc->client->pdu->s, buffer, Stream_Length(fragment));
Stream_SealLength(rpc->client->pdu->s);
rpc_client_on_pdu_received_event(rpc, rpc->client->pdu);
rpc->client->pdu = NULL;
return 0;
}
rpc->VirtualConnection->DefaultOutChannel->BytesReceived += header->common.frag_length;
rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow -= header->common.frag_length;
switch (header->common.ptype)
{
case PTYPE_RTS:
if (rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow < (rpc->ReceiveWindow / 2))
{
rts_send_flow_control_ack_pdu(rpc);
}
if (rpc->VirtualConnection->State < VIRTUAL_CONNECTION_STATE_OPENED)
if (!rpc_get_stub_data_info(rpc, buffer, &StubOffset, &StubLength))
{
WLog_ERR(TAG, "expected stub");
return -1;
}
if (StubLength == 4)
{
/* received a disconnect request from the server? */
if ((header->common.call_id == rpc->PipeCallId) && (header->common.pfc_flags & PFC_LAST_FRAG))
{
WLog_ERR(TAG, "warning: unhandled RTS PDU");
return 0;
TerminateEventArgs e;
rpc->result = *((UINT32*) &buffer[StubOffset]);
rpc->context->rdp->disconnect = TRUE;
rpc->transport->tsg->state = TSG_STATE_TUNNEL_CLOSE_PENDING;
EventArgsInit(&e, "freerdp");
e.code = 0;
PubSub_OnTerminate(rpc->context->pubSub, rpc->context, &e);
}
WLog_DBG(TAG, "Receiving Out-of-Sequence RTS PDU");
rts_recv_out_of_sequence_pdu(rpc, buffer, header->common.frag_length);
return 0;
}
case PTYPE_FAULT:
rpc_recv_fault_pdu(header);
return -1;
if (rpc->StubFragCount == 0)
rpc->StubCallId = header->common.call_id;
case PTYPE_RESPONSE:
break;
default:
WLog_ERR(TAG, "unexpected RPC PDU type %d", header->common.ptype);
return -1;
}
/* PTYPE_RESPONSE */
rpc->VirtualConnection->DefaultOutChannel->BytesReceived += header->common.frag_length;
rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow -= header->common.frag_length;
if (!rpc_get_stub_data_info(rpc, buffer, &StubOffset, &StubLength))
{
WLog_ERR(TAG, "expected stub");
return -1;
}
if (StubLength == 4)
{
/* received a disconnect request from the server? */
if ((header->common.call_id == rpc->PipeCallId) && (header->common.pfc_flags & PFC_LAST_FRAG))
if (rpc->StubCallId != header->common.call_id)
{
TerminateEventArgs e;
rpc->result = *((UINT32*) &buffer[StubOffset]);
rpc->context->rdp->disconnect = TRUE;
rpc->transport->tsg->state = TSG_STATE_TUNNEL_CLOSE_PENDING;
EventArgsInit(&e, "freerdp");
e.code = 0;
PubSub_OnTerminate(rpc->context->pubSub, rpc->context, &e);
WLog_ERR(TAG, "invalid call_id: actual: %d, expected: %d, frag_count: %d",
rpc->StubCallId, header->common.call_id, rpc->StubFragCount);
}
call = rpc_client_call_find_by_id(rpc, rpc->StubCallId);
if (!call)
return -1;
if (call->OpNum != TsProxySetupReceivePipeOpnum)
{
if (!rpc->client->pdu)
rpc->client->pdu = rpc_client_receive_pool_take(rpc);
pdu = rpc->client->pdu;
Stream_EnsureCapacity(pdu->s, header->response.alloc_hint);
Stream_Write(pdu->s, &buffer[StubOffset], StubLength);
rpc->StubFragCount++;
if (header->response.alloc_hint == StubLength)
{
pdu->Flags = RPC_PDU_FLAG_STUB;
pdu->Type = PTYPE_RESPONSE;
pdu->CallId = rpc->StubCallId;
Stream_SealLength(pdu->s);
rpc_client_on_pdu_received_event(rpc, pdu);
rpc->client->pdu = NULL;
rpc->StubFragCount = 0;
rpc->StubCallId = 0;
}
}
else
{
rpc_client_receive_pipe_write(rpc, &buffer[StubOffset], (size_t) StubLength);
rpc->StubFragCount++;
if (header->response.alloc_hint == StubLength)
{
rpc->StubFragCount = 0;
rpc->StubCallId = 0;
}
}
return 0;
}
if (rpc->StubFragCount == 0)
rpc->StubCallId = header->common.call_id;
if (rpc->StubCallId != header->common.call_id)
else if (header->common.ptype == PTYPE_RTS)
{
WLog_ERR(TAG, "invalid call_id: actual: %d, expected: %d, frag_count: %d",
rpc->StubCallId, header->common.call_id, rpc->StubFragCount);
if (rpc->State < RPC_CLIENT_STATE_CONTEXT_NEGOTIATED)
{
if (!rpc->client->pdu)
rpc->client->pdu = rpc_client_receive_pool_take(rpc);
pdu = rpc->client->pdu;
pdu->Flags = 0;
pdu->Type = header->common.ptype;
pdu->CallId = header->common.call_id;
Stream_EnsureCapacity(pdu->s, Stream_Length(fragment));
Stream_Write(pdu->s, buffer, Stream_Length(fragment));
Stream_SealLength(pdu->s);
rpc_client_on_pdu_received_event(rpc, pdu);
rpc->client->pdu = NULL;
return 0;
}
else
{
if (rpc->VirtualConnection->State < VIRTUAL_CONNECTION_STATE_OPENED)
WLog_ERR(TAG, "warning: unhandled RTS PDU");
WLog_DBG(TAG, "Receiving Out-of-Sequence RTS PDU");
rts_recv_out_of_sequence_pdu(rpc, buffer, header->common.frag_length);
}
}
Stream_EnsureCapacity(rpc->client->pdu->s, header->response.alloc_hint);
Stream_Write(rpc->client->pdu->s, &buffer[StubOffset], StubLength);
rpc->StubFragCount++;
if (rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow < (rpc->ReceiveWindow / 2))
else if (header->common.ptype == PTYPE_BIND_ACK)
{
rts_send_flow_control_ack_pdu(rpc);
}
if (!rpc->client->pdu)
rpc->client->pdu = rpc_client_receive_pool_take(rpc);
/**
* If alloc_hint is set to a nonzero value and a request or a response is fragmented into multiple
* PDUs, implementations of these extensions SHOULD set the alloc_hint field in every PDU to be the
* combined stub data length of all remaining fragment PDUs.
*/
pdu = rpc->client->pdu;
if (header->response.alloc_hint == StubLength)
{
rpc->client->pdu->Flags = RPC_PDU_FLAG_STUB;
rpc->client->pdu->Type = PTYPE_RESPONSE;
rpc->client->pdu->CallId = rpc->StubCallId;
Stream_SealLength(rpc->client->pdu->s);
rpc->StubFragCount = 0;
rpc->StubCallId = 0;
rpc_client_on_pdu_received_event(rpc, rpc->client->pdu);
pdu->Flags = 0;
pdu->Type = header->common.ptype;
pdu->CallId = header->common.call_id;
Stream_EnsureCapacity(pdu->s, Stream_Length(fragment));
Stream_Write(pdu->s, buffer, Stream_Length(fragment));
Stream_SealLength(pdu->s);
rpc_client_on_pdu_received_event(rpc, pdu);
rpc->client->pdu = NULL;
return 0;
}
else if (header->common.ptype == PTYPE_FAULT)
{
rpc_recv_fault_pdu(header);
return -1;
}
else
{
WLog_ERR(TAG, "unexpected RPC PDU type 0x%04X", header->common.ptype);
}
return 0;
}
@ -342,8 +355,8 @@ int rpc_client_on_read_event(rdpRpc* rpc)
Stream_SetPosition(rpc->client->ReceiveFragment, 0);
}
if (WaitForSingleObject(Queue_Event(rpc->client->SendQueue), 0) == WAIT_OBJECT_0)
break;
//if (WaitForSingleObject(Queue_Event(rpc->client->SendQueue), 0) == WAIT_OBJECT_0)
// break;
}
return 0;

View File

@ -22,16 +22,15 @@
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winpr/crt.h>
#include <winpr/ndr.h>
#include <winpr/error.h>
#include <winpr/print.h>
#include <winpr/stream.h>
#include <freerdp/log.h>
#include "rpc_bind.h"
#include "rpc_client.h"
#include "tsg.h"
@ -43,16 +42,6 @@
* RPC NDR Interface Reference: http://msdn.microsoft.com/en-us/library/windows/desktop/hh802752/
*/
/* this might be a verification trailer */
BYTE TsProxyCreateTunnelUnknownTrailerBytes[60] =
{
0x8A, 0xE3, 0x13, 0x71, 0x02, 0xF4, 0x36, 0x71, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00,
0x02, 0x40, 0x28, 0x00, 0xDD, 0x65, 0xE2, 0x44, 0xAF, 0x7D, 0xCD, 0x42, 0x85, 0x60, 0x3C, 0xDB,
0x6E, 0x7A, 0x27, 0x29, 0x01, 0x00, 0x03, 0x00, 0x04, 0x5D, 0x88, 0x8A, 0xEB, 0x1C, 0xC9, 0x11,
0x9F, 0xE8, 0x08, 0x00, 0x2B, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00
};
DWORD TsProxySendToServer(handle_t IDL_handle, byte pRpcMessage[], UINT32 count, UINT32* lengths)
{
wStream* s;
@ -68,6 +57,7 @@ DWORD TsProxySendToServer(handle_t IDL_handle, byte pRpcMessage[], UINT32 count,
UINT32 buffer3Length;
UINT32 numBuffers = 0;
UINT32 totalDataBytes = 0;
tsg = (rdpTsg*) IDL_handle;
buffer1Length = buffer2Length = buffer3Length = 0;
@ -186,7 +176,47 @@ BOOL TsProxyCreateTunnelWriteRequest(rdpTsg* tsg)
*/
*((UINT32*) &buffer[44]) = NapCapabilities; /* capabilities */
CopyMemory(&buffer[48], TsProxyCreateTunnelUnknownTrailerBytes, 60);
//CopyMemory(&buffer[48], TsProxyCreateTunnelUnknownTrailerBytes, 60);
/**
* The following 60-byte structure is apparently undocumented,
* but parts of it can be matched to known C706 data structures.
*/
/*
* 8-byte constant (8A E3 13 71 02 F4 36 71) also observed here:
* http://lists.samba.org/archive/cifs-protocol/2010-July/001543.html
*/
buffer[48] = 0x8A;
buffer[49] = 0xE3;
buffer[50] = 0x13;
buffer[51] = 0x71;
buffer[52] = 0x02;
buffer[53] = 0xF4;
buffer[54] = 0x36;
buffer[55] = 0x71;
*((UINT32*) &buffer[56]) = 0x00040001; /* 1.4 (version?) */
*((UINT32*) &buffer[60]) = 0x00000001; /* 1 (element count?) */
/* p_cont_list_t */
buffer[64] = 2; /* ncontext_elem */
buffer[65] = 0x40; /* reserved1 */
*((UINT16*) &buffer[66]) = 0x0028; /* reserved2 */
/* p_syntax_id_t */
CopyMemory(&buffer[68], &TSGU_UUID, sizeof(p_uuid_t));
*((UINT32*) &buffer[84]) = TSGU_SYNTAX_IF_VERSION;
/* p_syntax_id_t */
CopyMemory(&buffer[88], &NDR_UUID, sizeof(p_uuid_t));
*((UINT32*) &buffer[104]) = NDR_SYNTAX_IF_VERSION;
status = rpc_write(rpc, buffer, length, TsProxyCreateTunnelOpnum);
if (status <= 0)