From 3b37d2262a43b6d1ceb76ed4c7ecb8e331f871ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sat, 21 Apr 2012 16:18:07 -0400 Subject: [PATCH] libfreerdp-core: separate RTS and RPC PDU receiving --- libfreerdp-core/http.c | 11 ++ libfreerdp-core/http.h | 2 + libfreerdp-core/rpc.c | 440 +++++++++-------------------------------- libfreerdp-core/rpc.h | 36 +++- libfreerdp-core/rts.c | 358 ++++++++++++++++++++++++++++++++- libfreerdp-core/rts.h | 17 +- 6 files changed, 505 insertions(+), 359 deletions(-) diff --git a/libfreerdp-core/http.c b/libfreerdp-core/http.c index ddba2c17c..985d185ff 100644 --- a/libfreerdp-core/http.c +++ b/libfreerdp-core/http.c @@ -287,6 +287,17 @@ void http_response_parse_header(HttpResponse* http_response) } } +void http_response_print(HttpResponse* http_response) +{ + int i; + + for (i = 0; i < http_response->count; i++) + { + printf("%s\n", http_response->lines[i]); + } + printf("\n"); +} + HttpResponse* http_response_recv(rdpTls* tls) { uint8* p; diff --git a/libfreerdp-core/http.h b/libfreerdp-core/http.h index 06f50e97d..2b3bee83c 100644 --- a/libfreerdp-core/http.h +++ b/libfreerdp-core/http.h @@ -91,6 +91,8 @@ struct _http_response char* Content; }; +void http_response_print(HttpResponse* http_response); + HttpResponse* http_response_recv(rdpTls* tls); HttpResponse* http_response_new(); diff --git a/libfreerdp-core/rpc.c b/libfreerdp-core/rpc.c index 28ed6345f..7bff866f1 100644 --- a/libfreerdp-core/rpc.c +++ b/libfreerdp-core/rpc.c @@ -302,6 +302,21 @@ boolean rpc_in_connect_http(rdpRpc* rpc) return true; } +void rpc_pdu_header_read(STREAM* s, RPC_PDU_HEADER* header) +{ + stream_read_uint8(s, header->rpc_vers); /* rpc_vers (1 byte) */ + stream_read_uint8(s, header->rpc_vers_minor); /* rpc_vers_minor (1 byte) */ + stream_read_uint8(s, header->ptype); /* PTYPE (1 byte) */ + stream_read_uint8(s, header->pfc_flags); /* pfc_flags (1 byte) */ + stream_read_uint8(s, header->packed_drep[0]); /* packet_drep[0] (1 byte) */ + stream_read_uint8(s, header->packed_drep[1]); /* packet_drep[1] (1 byte) */ + stream_read_uint8(s, header->packed_drep[2]); /* packet_drep[2] (1 byte) */ + stream_read_uint8(s, header->packed_drep[3]); /* packet_drep[3] (1 byte) */ + stream_read_uint16(s, header->frag_length); /* frag_length (2 bytes) */ + stream_read_uint16(s, header->auth_length); /* auth_length (2 bytes) */ + stream_read_uint32(s, header->call_id); /* call_id (4 bytes) */ +} + int rpc_out_write(rdpRpc* rpc, uint8* data, int length) { int status; @@ -337,148 +352,7 @@ int rpc_in_write(rdpRpc* rpc, uint8* data, int length) return status; } -uint8* rpc_create_cookie() -{ - uint8* ret = xmalloc(16); - RAND_pseudo_bytes(ret, 16); - return ret; -} - -void rpc_generate_cookie(uint8* cookie) -{ - RAND_pseudo_bytes(cookie, 16); -} - -boolean rpc_send_CONN_A1_pdu(rdpRpc* rpc) -{ - STREAM* s; - RTS_PDU_HEADER header; - uint32 ReceiveWindowSize; - uint8* OUTChannelCookie; - uint8* VirtualConnectionCookie; - - header.rpc_vers = 5; - header.rpc_vers_minor = 0; - header.ptype = PTYPE_RTS; - header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG; - header.packed_drep[0] = 0x10; - header.packed_drep[1] = 0x00; - header.packed_drep[2] = 0x00; - header.packed_drep[3] = 0x00; - header.frag_length = 76; - header.auth_length = 0; - header.call_id = 0; - header.flags = 0; - header.numberOfCommands = 4; - - DEBUG_RPC("Sending CONN_A1 RTS PDU"); - - s = stream_new(header.frag_length); - - rpc_generate_cookie((uint8*) &(rpc->VirtualConnection->Cookie)); - rpc_generate_cookie((uint8*) &(rpc->VirtualConnection->DefaultOutChannelCookie)); - - VirtualConnectionCookie = (uint8*) &(rpc->VirtualConnection->Cookie); - OUTChannelCookie = (uint8*) &(rpc->VirtualConnection->DefaultOutChannelCookie); - ReceiveWindowSize = rpc->VirtualConnection->DefaultOutChannel->ReceiveWindow; - - rts_pdu_header_write(s, &header); /* RTS Header (20 bytes) */ - rts_version_command_write(s); /* Version (8 bytes) */ - rts_cookie_command_write(s, VirtualConnectionCookie); /* VirtualConnectionCookie (20 bytes) */ - rts_cookie_command_write(s, OUTChannelCookie); /* OUTChannelCookie (20 bytes) */ - rts_receive_window_size_command_write(s, ReceiveWindowSize); /* ReceiveWindowSize (8 bytes) */ - stream_seal(s); - - rpc_out_write(rpc, s->data, s->size); - - stream_free(s); - - return true; -} - -boolean rpc_send_CONN_B1_pdu(rdpRpc* rpc) -{ - STREAM* s; - RTS_PDU_HEADER header; - uint8* INChannelCookie; - uint8* AssociationGroupId; - uint8* VirtualConnectionCookie; - - header.rpc_vers = 5; - header.rpc_vers_minor = 0; - header.ptype = PTYPE_RTS; - header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG; - header.packed_drep[0] = 0x10; - header.packed_drep[1] = 0x00; - header.packed_drep[2] = 0x00; - header.packed_drep[3] = 0x00; - header.frag_length = 104; - header.auth_length = 0; - header.call_id = 0; - header.flags = 0; - header.numberOfCommands = 6; - - DEBUG_RPC("Sending CONN_B1 RTS PDU"); - - s = stream_new(header.frag_length); - - rpc_generate_cookie((uint8*) &(rpc->VirtualConnection->DefaultInChannelCookie)); - rpc_generate_cookie((uint8*) &(rpc->VirtualConnection->AssociationGroupId)); - - VirtualConnectionCookie = (uint8*) &(rpc->VirtualConnection->Cookie); - INChannelCookie = (uint8*) &(rpc->VirtualConnection->DefaultInChannelCookie); - AssociationGroupId = (uint8*) &(rpc->VirtualConnection->AssociationGroupId); - - rts_pdu_header_write(s, &header); /* RTS Header (20 bytes) */ - rts_version_command_write(s); /* Version (8 bytes) */ - rts_cookie_command_write(s, VirtualConnectionCookie); /* VirtualConnectionCookie (20 bytes) */ - rts_cookie_command_write(s, INChannelCookie); /* INChannelCookie (20 bytes) */ - rts_channel_lifetime_command_write(s, 0x40000000); /* ChannelLifetime (8 bytes) */ - rts_client_keepalive_command_write(s, 0x000493E0); /* ClientKeepalive (8 bytes) */ - rts_association_group_id_command_write(s, AssociationGroupId); /* AssociationGroupId (20 bytes) */ - stream_seal(s); - - rpc_in_write(rpc, s->data, s->size); - - stream_free(s); - - return true; -} - -boolean rpc_send_keep_alive_pdu(rdpRpc* rpc) -{ - STREAM* s; - RTS_PDU_HEADER header; - - header.rpc_vers = 5; - header.rpc_vers_minor = 0; - header.ptype = PTYPE_RTS; - header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG; - header.packed_drep[0] = 0x10; - header.packed_drep[1] = 0x00; - header.packed_drep[2] = 0x00; - header.packed_drep[3] = 0x00; - header.frag_length = 28; - header.auth_length = 0; - header.call_id = 0; - header.flags = 2; - header.numberOfCommands = 1; - - DEBUG_RPC("Sending Keep-Alive RTS PDU"); - - s = stream_new(header.frag_length); - rts_pdu_header_write(s, &header); /* RTS Header (20 bytes) */ - rts_client_keepalive_command_write(s, 0x00007530); /* ClientKeepalive (8 bytes) */ - stream_seal(s); - - rpc_in_write(rpc, s->data, s->size); - - stream_free(s); - - return true; -} - -boolean rpc_in_send_bind(rdpRpc* rpc) +boolean rpc_send_bind_pdu(rdpRpc* rpc) { STREAM* pdu; rpcconn_bind_hdr_t* bind_pdu; @@ -609,13 +483,50 @@ boolean rpc_in_send_bind(rdpRpc* rpc) return true; } -boolean rpc_in_send_rpc_auth_3(rdpRpc* rpc) +int rpc_recv_bind_ack_pdu(rdpRpc* rpc) +{ + STREAM* s; + uint16 frag_length; + uint16 auth_length; + STREAM* ntlmssp_stream; + RPC_PDU_HEADER header; + int pdu_length = 0x8FFF; /* 32KB buffer */ + uint8* pdu = xmalloc(pdu_length); + int status = rpc_out_read(rpc, pdu, pdu_length); + + DEBUG_RPC("TODO: complete NTLM integration"); + + if (status > 0) + { + s = stream_new(0); + stream_attach(s, pdu, pdu_length); + rpc_pdu_header_read(s, &header); + + frag_length = header.frag_length; + auth_length = header.auth_length; + + printf("frag_length:%d auth_length:%d\n", frag_length, auth_length); + + ntlmssp_stream = stream_new(0xFFFF); + stream_write(ntlmssp_stream, (pdu + (frag_length - auth_length)), auth_length); + ntlmssp_stream->p = ntlmssp_stream->data; + + //ntlmssp_recv(rpc->ntlmssp, ntlmssp_stream); + + stream_free(ntlmssp_stream); + } + + xfree(pdu); + return status; +} + +boolean rpc_send_rpc_auth_3_pdu(rdpRpc* rpc) { STREAM* pdu; rpcconn_rpc_auth_3_hdr_t* rpc_auth_3_pdu; STREAM* ntlm_stream = stream_new(0xFFFF); - DEBUG_RPC("Sending auth3 PDU"); + DEBUG_RPC("Sending auth_3 PDU"); ntlm_authenticate(rpc->ntlm); ntlm_stream->size = rpc->ntlm->outputBuffer.cbBuffer; @@ -633,6 +544,7 @@ boolean rpc_in_send_rpc_auth_3(rdpRpc* rpc) rpc_auth_3_pdu->frag_length = 28 + ntlm_stream->size; rpc_auth_3_pdu->auth_length = ntlm_stream->size; rpc_auth_3_pdu->call_id = 2; + rpc_auth_3_pdu->max_xmit_frag = 0x0FF8; rpc_auth_3_pdu->max_recv_frag = 0x0FF8; rpc_auth_3_pdu->auth_verifier.auth_pad = NULL; /* align(4); size_is(auth_pad_length) p */ @@ -663,82 +575,6 @@ boolean rpc_in_send_rpc_auth_3(rdpRpc* rpc) return true; } -boolean rpc_send_flow_control_ack_pdu(rdpRpc* rpc) -{ - STREAM* s; - RTS_PDU_HEADER header; - uint32 BytesReceived; - uint32 AvailableWindow; - uint8* ChannelCookie; - - header.rpc_vers = 5; - header.rpc_vers_minor = 0; - header.ptype = PTYPE_RTS; - header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG; - header.packed_drep[0] = 0x10; - header.packed_drep[1] = 0x00; - header.packed_drep[2] = 0x00; - header.packed_drep[3] = 0x00; - header.frag_length = 56; - header.auth_length = 0; - header.call_id = 0; - header.flags = 2; - header.numberOfCommands = 2; - - DEBUG_RPC("Sending FlowControlAck RTS PDU"); - - BytesReceived = rpc->VirtualConnection->DefaultOutChannel->RecipientBytesReceived; - AvailableWindow = rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow; - ChannelCookie = (uint8*) &(rpc->VirtualConnection->DefaultOutChannelCookie); - - s = stream_new(header.frag_length); - rts_pdu_header_write(s, &header); /* RTS Header (20 bytes) */ - rts_destination_command_write(s, FDOutProxy); /* Destination Command (8 bytes) */ - - /* FlowControlAck Command (28 bytes) */ - rts_flow_control_ack_command_write(s, BytesReceived, AvailableWindow, ChannelCookie); - - stream_seal(s); - - rpc_in_write(rpc, s->data, s->size); - - stream_free(s); - - return true; -} - -boolean rpc_send_ping_pdu(rdpRpc* rpc) -{ - STREAM* s; - RTS_PDU_HEADER header; - - header.rpc_vers = 5; - header.rpc_vers_minor = 0; - header.ptype = PTYPE_RTS; - header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG; - header.packed_drep[0] = 0x10; - header.packed_drep[1] = 0x00; - header.packed_drep[2] = 0x00; - header.packed_drep[3] = 0x00; - header.frag_length = 20; - header.auth_length = 0; - header.call_id = 0; - header.flags = 1; - header.numberOfCommands = 0; - - DEBUG_RPC("Sending Ping RTS PDU"); - - s = stream_new(header.frag_length); - rts_pdu_header_write(s, &header); /* RTS Header (20 bytes) */ - stream_seal(s); - - rpc_in_write(rpc, s->data, s->size); - - stream_free(s); - - return true; -} - int rpc_out_read_http_header(rdpRpc* rpc) { int status = 0; @@ -755,27 +591,33 @@ int rpc_out_read(rdpRpc* rpc, uint8* data, int length) STREAM* s; int status; uint8* pdu; - uint8 ptype; - uint16 frag_length; + int content_length; + RPC_PDU_HEADER header; rdpTls* tls_out = rpc->tls_out; if (rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow < 0x00008FFF) /* Just a simple workaround */ - rpc_send_flow_control_ack_pdu(rpc); /* Send FlowControlAck every time AW reaches the half */ + rts_send_flow_control_ack_pdu(rpc); /* Send FlowControlAck every time AvailableWindow reaches the half */ pdu = xmalloc(0xFFFF); - status = tls_read(tls_out, pdu, 10); + status = tls_read(tls_out, pdu, 16); /* read first 16 bytes to get RPC PDU Header */ - if (status <= 0) /* read first 10 bytes to get the frag_length value */ + if (status <= 0) { xfree(pdu); return status; } - ptype = *(pdu + 2); - frag_length = *((uint16*) (pdu + 8)); + s = stream_new(0); + stream_attach(s, pdu, 16); - status = tls_read(tls_out, pdu + 10, frag_length - 10); + rpc_pdu_header_read(s, &header); + + stream_detach(s); + stream_free(s); + + content_length = header.frag_length - 16; + status = tls_read(tls_out, pdu + 16, content_length); if (status < 0) { @@ -783,74 +625,35 @@ int rpc_out_read(rdpRpc* rpc, uint8* data, int length) return status; } - if (ptype == PTYPE_RTS) /* RTS PDU */ + if (header.ptype == PTYPE_RTS) /* RTS PDU */ { - s = stream_new(0); - stream_attach(s, pdu, frag_length); - rts_pdu_recv(rpc, s); - xfree(pdu); - return 0; + printf("rpc_out_read error: Unexpected RTS PDU\n"); + return -1; } else { /* RTS PDUs are not subject to flow control */ - rpc->VirtualConnection->DefaultOutChannel->RecipientBytesReceived += frag_length; - rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow -= frag_length; + rpc->VirtualConnection->DefaultOutChannel->RecipientBytesReceived += header.frag_length; + rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow -= header.frag_length; } - if (length < frag_length) + if (length < header.frag_length) { - printf("rpc_out_read(): Error! Given buffer is to small. Received data fits not in.\n"); - xfree(pdu); - return -1; /* TODO add buffer for storing remaining data for the next read in case destination buffer is too small */ - } - - memcpy(data, pdu, frag_length); - - if (strncmp((char*) pdu, "HTTP", 4) == 0) - { - printf("\n%s", (char*) pdu); + printf("rpc_out_read error! receive buffer is not large enough\n"); return -1; } + memcpy(data, pdu, header.frag_length); + #ifdef WITH_DEBUG_RPC - printf("rpc_out_read(): length: %d\n", frag_length); - freerdp_hexdump(data, frag_length); + printf("rpc_out_read(): length: %d\n", header.frag_length); + freerdp_hexdump(data, header.frag_length); printf("\n"); #endif xfree(pdu); - return frag_length; -} - -int rpc_out_recv_bind_ack(rdpRpc* rpc) -{ - uint16 frag_length; - uint16 auth_length; - STREAM* ntlmssp_stream; - int pdu_length = 0x8FFF; /* 32KB buffer */ - uint8* pdu = xmalloc(pdu_length); - int status = rpc_out_read(rpc, pdu, pdu_length); - - DEBUG_RPC("TODO: complete NTLM integration"); - - if (status > 0) - { - frag_length = *((uint16*)(pdu + 8)); - auth_length = *((uint16*)(pdu + 10)); - - ntlmssp_stream = stream_new(0xFFFF); - stream_write(ntlmssp_stream, (pdu + (frag_length - auth_length)), auth_length); - ntlmssp_stream->p = ntlmssp_stream->data; - - //ntlmssp_recv(rpc->ntlmssp, ntlmssp_stream); - - stream_free(ntlmssp_stream); - } - - xfree(pdu); - return status; + return header.frag_length; } int rpc_write(rdpRpc* rpc, uint8* data, int length, uint16 opnum) @@ -928,7 +731,7 @@ int rpc_write(rdpRpc* rpc, uint8* data, int length, uint16 opnum) if (status < 0) { - printf("rpc_write(): Error! rcp_in_write returned negative value.\n"); + printf("rpc_write(): Error! rpc_in_write returned negative value.\n"); return status; } @@ -1018,92 +821,32 @@ int rpc_read(rdpRpc* rpc, uint8* data, int length) return read; } -/** - * Connection Establishment\n - * - * Client Outbound Proxy Inbound Proxy Server\n - * | | | |\n - * |-----------------IN Channel Request--------------->| |\n - * |---OUT Channel Request-->| |<-Legacy Server Response-|\n - * | |<--------------Legacy Server Response--------------|\n - * | | | |\n - * |---------CONN_A1-------->| | |\n - * |----------------------CONN_B1--------------------->| |\n - * | |----------------------CONN_A2--------------------->|\n - * | | | |\n - * |<--OUT Channel Response--| |---------CONN_B2-------->|\n - * |<--------CONN_A3---------| | |\n - * | |<---------------------CONN_C1----------------------|\n - * | | |<--------CONN_B3---------|\n - * |<--------CONN_C2---------| | |\n - * | | | |\n - * - */ - boolean rpc_connect(rdpRpc* rpc) { - int status; - uint8* pdu; - int pdu_length; - - pdu_length = 0xFFFF; - pdu = xmalloc(pdu_length); - - if (!rpc_out_connect_http(rpc)) + if (!rts_connect(rpc)) { - printf("rpc_out_connect_http error!\n"); + printf("rts_connect error!\n"); return false; } - if (!rpc_send_CONN_A1_pdu(rpc)) + if (!rpc_send_bind_pdu(rpc)) { - printf("rpc_send_CONN_A1_pdu error!\n"); + printf("rpc_send_bind_pdu error!\n"); return false; } - if (!rpc_in_connect_http(rpc)) + if (!rpc_recv_bind_ack_pdu(rpc)) { - printf("rpc_in_connect_http error!\n"); + printf("rpc_recv_bind_ack_pdu error!\n"); return false; } - if (!rpc_send_CONN_B1_pdu(rpc)) + if (!rpc_send_rpc_auth_3_pdu(rpc)) { - printf("rpc_send_CONN_B1_pdu error!\n"); + printf("rpc_send_rpc_auth_3 error!\n"); return false; } - /* Receive OUT Channel Response */ - status = rpc_out_read(rpc, pdu, pdu_length); - - /* Receive CONN_A3 RTS PDU */ - status = rpc_out_read(rpc, pdu, pdu_length); - - /* Receive CONN_C2 RTS PDU */ - status = rpc_out_read(rpc, pdu, pdu_length); - - /* [MS-RPCH] 3.2.1.5.3.1 Connection Establishment - * at this point VirtualChannel is created - */ - if (!rpc_in_send_bind(rpc)) - { - printf("rpc_in_send_bind fault!\n"); - return false; - } - - if (!rpc_out_recv_bind_ack(rpc)) - { - printf("rpc_out_recv_bind_ack fault!\n"); - return false; - } - - if (!rpc_in_send_rpc_auth_3(rpc)) - { - printf("rpc_out_send_rpc_auth_3 fault!\n"); - return false; - } - - xfree(pdu); return true; } @@ -1125,6 +868,7 @@ RpcVirtualConnection* rpc_client_virtual_connection_new(rdpRpc* rpc) if (virtual_connection != NULL) { + virtual_connection->State = VIRTUAL_CONNECTION_STATE_INITIAL; virtual_connection->DefaultInChannel = xnew(RpcInChannel); virtual_connection->DefaultOutChannel = xnew(RpcOutChannel); rpc_client_virtual_connection_init(rpc, virtual_connection); diff --git a/libfreerdp-core/rpc.h b/libfreerdp-core/rpc.h index b3399b733..352953bbc 100644 --- a/libfreerdp-core/rpc.h +++ b/libfreerdp-core/rpc.h @@ -41,6 +41,19 @@ typedef struct rdp_rpc_http rdpRpcHTTP; #include #include +struct _rpc_pdu_header +{ + uint8 rpc_vers; + uint8 rpc_vers_minor; + uint8 ptype; + uint8 pfc_flags; + uint8 packed_drep[4]; + uint16 frag_length; + uint16 auth_length; + uint32 call_id; +}; +typedef struct _rpc_pdu_header RPC_PDU_HEADER; + typedef uint16 p_context_id_t; typedef struct { @@ -588,10 +601,21 @@ typedef struct rpc_out_channel RpcOutChannel; /* Client Virtual Connection */ +enum _VIRTUAL_CONNECTION_STATE +{ + VIRTUAL_CONNECTION_STATE_INITIAL, + VIRTUAL_CONNECTION_STATE_OUT_CHANNEL_WAIT, + VIRTUAL_CONNECTION_STATE_WAIT_A3W, + VIRTUAL_CONNECTION_STATE_WAIT_C2, + VIRTUAL_CONNECTION_STATE_OPENED, + VIRTUAL_CONNECTION_STATE_FINAL +}; +typedef enum _VIRTUAL_CONNECTION_STATE VIRTUAL_CONNECTION_STATE; + struct rpc_virtual_connection { uint8 Cookie[16]; /* Virtual Connection Cookie */ - uint32 State; /* Virtual Connection State */ + VIRTUAL_CONNECTION_STATE State; /* Virtual Connection State */ RpcInChannel* DefaultInChannel; /* Default IN Channel */ RpcInChannel* NonDefaultInChannel; /* Non-Default IN Channel */ uint8 DefaultInChannelCookie[16]; /* Default IN Channel Cookie */ @@ -642,7 +666,15 @@ void ntlm_free(rdpNtlm* ntlm); boolean rpc_attach(rdpRpc* rpc, rdpTcp* tcp_in, rdpTcp* tcp_out, rdpTls* tls_in, rdpTls* tls_out); boolean rpc_connect(rdpRpc* rpc); -boolean rpc_send_keep_alive_pdu(rdpRpc* rpc); +boolean rpc_out_connect_http(rdpRpc* rpc); +boolean rpc_in_connect_http(rdpRpc* rpc); + +void rpc_pdu_header_read(STREAM* s, RPC_PDU_HEADER* header); + +int rpc_out_write(rdpRpc* rpc, uint8* data, int length); +int rpc_in_write(rdpRpc* rpc, uint8* data, int length); + +int rpc_out_read(rdpRpc* rpc, uint8* data, int length); int rpc_write(rdpRpc* rpc, uint8* data, int length, uint16 opnum); int rpc_read(rdpRpc* rpc, uint8* data, int length); diff --git a/libfreerdp-core/rts.c b/libfreerdp-core/rts.c index bc6b39948..618cfa2de 100644 --- a/libfreerdp-core/rts.c +++ b/libfreerdp-core/rts.c @@ -19,6 +19,81 @@ #include "rts.h" +/** + * Connection Establishment\n + * + * Client Outbound Proxy Inbound Proxy Server\n + * | | | |\n + * |-----------------IN Channel Request--------------->| |\n + * |---OUT Channel Request-->| |<-Legacy Server Response-|\n + * | |<--------------Legacy Server Response--------------|\n + * | | | |\n + * |---------CONN_A1-------->| | |\n + * |----------------------CONN_B1--------------------->| |\n + * | |----------------------CONN_A2--------------------->|\n + * | | | |\n + * |<--OUT Channel Response--| |---------CONN_B2-------->|\n + * |<--------CONN_A3---------| | |\n + * | |<---------------------CONN_C1----------------------|\n + * | | |<--------CONN_B3---------|\n + * |<--------CONN_C2---------| | |\n + * | | | |\n + * + */ + +boolean rts_connect(rdpRpc* rpc) +{ + int status; + RTS_PDU rts_pdu; + HttpResponse* http_response; + + if (!rpc_out_connect_http(rpc)) + { + printf("rpc_out_connect_http error!\n"); + return false; + } + + if (!rts_send_CONN_A1_pdu(rpc)) + { + printf("rpc_send_CONN_A1_pdu error!\n"); + return false; + } + + if (!rpc_in_connect_http(rpc)) + { + printf("rpc_in_connect_http error!\n"); + return false; + } + + if (!rts_send_CONN_B1_pdu(rpc)) + { + printf("rpc_send_CONN_B1_pdu error!\n"); + return false; + } + + /* Receive OUT Channel Response */ + http_response = http_response_recv(rpc->tls_out); + + if (http_response->StatusCode != 200) + { + printf("rts_connect error!\n"); + http_response_print(http_response); + return false; + } + + http_response_print(http_response); + + http_response_free(http_response); + + /* Receive CONN_A3 RTS PDU */ + status = rts_recv_pdu(rpc, &rts_pdu); + + /* Receive CONN_C2 RTS PDU */ + status = rts_recv_pdu(rpc, &rts_pdu); + + return true; +} + #ifdef WITH_DEBUG_RTS static const char* const RTS_CMD_STRINGS[] = @@ -272,23 +347,234 @@ void rts_ping_traffic_sent_notify_command_write(STREAM* s, uint32 PingTrafficSen stream_write_uint32(s, PingTrafficSent); /* PingTrafficSent (4 bytes) */ } -int rts_pdu_recv(rdpRpc* rpc, STREAM* s) +void rpc_generate_cookie(uint8* cookie) { - int i; - uint32 CommandType; + RAND_pseudo_bytes(cookie, 16); +} + +boolean rts_send_CONN_A1_pdu(rdpRpc* rpc) +{ + STREAM* s; + RTS_PDU_HEADER header; + uint32 ReceiveWindowSize; + uint8* OUTChannelCookie; + uint8* VirtualConnectionCookie; + + header.rpc_vers = 5; + header.rpc_vers_minor = 0; + header.ptype = PTYPE_RTS; + header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG; + header.packed_drep[0] = 0x10; + header.packed_drep[1] = 0x00; + header.packed_drep[2] = 0x00; + header.packed_drep[3] = 0x00; + header.frag_length = 76; + header.auth_length = 0; + header.call_id = 0; + header.flags = 0; + header.numberOfCommands = 4; + + DEBUG_RPC("Sending CONN_A1 RTS PDU"); + + s = stream_new(header.frag_length); + + rpc_generate_cookie((uint8*) &(rpc->VirtualConnection->Cookie)); + rpc_generate_cookie((uint8*) &(rpc->VirtualConnection->DefaultOutChannelCookie)); + + VirtualConnectionCookie = (uint8*) &(rpc->VirtualConnection->Cookie); + OUTChannelCookie = (uint8*) &(rpc->VirtualConnection->DefaultOutChannelCookie); + ReceiveWindowSize = rpc->VirtualConnection->DefaultOutChannel->ReceiveWindow; + + rts_pdu_header_write(s, &header); /* RTS Header (20 bytes) */ + rts_version_command_write(s); /* Version (8 bytes) */ + rts_cookie_command_write(s, VirtualConnectionCookie); /* VirtualConnectionCookie (20 bytes) */ + rts_cookie_command_write(s, OUTChannelCookie); /* OUTChannelCookie (20 bytes) */ + rts_receive_window_size_command_write(s, ReceiveWindowSize); /* ReceiveWindowSize (8 bytes) */ + stream_seal(s); + + rpc_out_write(rpc, s->data, s->size); + + stream_free(s); + + return true; +} + +boolean rts_send_CONN_B1_pdu(rdpRpc* rpc) +{ + STREAM* s; + RTS_PDU_HEADER header; + uint8* INChannelCookie; + uint8* AssociationGroupId; + uint8* VirtualConnectionCookie; + + header.rpc_vers = 5; + header.rpc_vers_minor = 0; + header.ptype = PTYPE_RTS; + header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG; + header.packed_drep[0] = 0x10; + header.packed_drep[1] = 0x00; + header.packed_drep[2] = 0x00; + header.packed_drep[3] = 0x00; + header.frag_length = 104; + header.auth_length = 0; + header.call_id = 0; + header.flags = 0; + header.numberOfCommands = 6; + + DEBUG_RPC("Sending CONN_B1 RTS PDU"); + + s = stream_new(header.frag_length); + + rpc_generate_cookie((uint8*) &(rpc->VirtualConnection->DefaultInChannelCookie)); + rpc_generate_cookie((uint8*) &(rpc->VirtualConnection->AssociationGroupId)); + + VirtualConnectionCookie = (uint8*) &(rpc->VirtualConnection->Cookie); + INChannelCookie = (uint8*) &(rpc->VirtualConnection->DefaultInChannelCookie); + AssociationGroupId = (uint8*) &(rpc->VirtualConnection->AssociationGroupId); + + rts_pdu_header_write(s, &header); /* RTS Header (20 bytes) */ + rts_version_command_write(s); /* Version (8 bytes) */ + rts_cookie_command_write(s, VirtualConnectionCookie); /* VirtualConnectionCookie (20 bytes) */ + rts_cookie_command_write(s, INChannelCookie); /* INChannelCookie (20 bytes) */ + rts_channel_lifetime_command_write(s, 0x40000000); /* ChannelLifetime (8 bytes) */ + rts_client_keepalive_command_write(s, 0x000493E0); /* ClientKeepalive (8 bytes) */ + rts_association_group_id_command_write(s, AssociationGroupId); /* AssociationGroupId (20 bytes) */ + stream_seal(s); + + rpc_in_write(rpc, s->data, s->size); + + stream_free(s); + + return true; +} + +boolean rts_send_keep_alive_pdu(rdpRpc* rpc) +{ + STREAM* s; RTS_PDU_HEADER header; - rts_pdu_header_read(s, &header); + header.rpc_vers = 5; + header.rpc_vers_minor = 0; + header.ptype = PTYPE_RTS; + header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG; + header.packed_drep[0] = 0x10; + header.packed_drep[1] = 0x00; + header.packed_drep[2] = 0x00; + header.packed_drep[3] = 0x00; + header.frag_length = 28; + header.auth_length = 0; + header.call_id = 0; + header.flags = 2; + header.numberOfCommands = 1; - DEBUG_RTS("numberOfCommands:%d", header.numberOfCommands); + DEBUG_RPC("Sending Keep-Alive RTS PDU"); - if (header.flags & RTS_FLAG_PING) + s = stream_new(header.frag_length); + rts_pdu_header_write(s, &header); /* RTS Header (20 bytes) */ + rts_client_keepalive_command_write(s, 0x00007530); /* ClientKeepalive (8 bytes) */ + stream_seal(s); + + rpc_in_write(rpc, s->data, s->size); + + stream_free(s); + + return true; +} + +boolean rts_send_flow_control_ack_pdu(rdpRpc* rpc) +{ + STREAM* s; + RTS_PDU_HEADER header; + uint32 BytesReceived; + uint32 AvailableWindow; + uint8* ChannelCookie; + + header.rpc_vers = 5; + header.rpc_vers_minor = 0; + header.ptype = PTYPE_RTS; + header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG; + header.packed_drep[0] = 0x10; + header.packed_drep[1] = 0x00; + header.packed_drep[2] = 0x00; + header.packed_drep[3] = 0x00; + header.frag_length = 56; + header.auth_length = 0; + header.call_id = 0; + header.flags = 2; + header.numberOfCommands = 2; + + DEBUG_RPC("Sending FlowControlAck RTS PDU"); + + BytesReceived = rpc->VirtualConnection->DefaultOutChannel->RecipientBytesReceived; + AvailableWindow = rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow; + ChannelCookie = (uint8*) &(rpc->VirtualConnection->DefaultOutChannelCookie); + + s = stream_new(header.frag_length); + rts_pdu_header_write(s, &header); /* RTS Header (20 bytes) */ + rts_destination_command_write(s, FDOutProxy); /* Destination Command (8 bytes) */ + + /* FlowControlAck Command (28 bytes) */ + rts_flow_control_ack_command_write(s, BytesReceived, AvailableWindow, ChannelCookie); + + stream_seal(s); + + rpc_in_write(rpc, s->data, s->size); + + stream_free(s); + + return true; +} + +boolean rts_send_ping_pdu(rdpRpc* rpc) +{ + STREAM* s; + RTS_PDU_HEADER header; + + header.rpc_vers = 5; + header.rpc_vers_minor = 0; + header.ptype = PTYPE_RTS; + header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG; + header.packed_drep[0] = 0x10; + header.packed_drep[1] = 0x00; + header.packed_drep[2] = 0x00; + header.packed_drep[3] = 0x00; + header.frag_length = 20; + header.auth_length = 0; + header.call_id = 0; + header.flags = 1; + header.numberOfCommands = 0; + + DEBUG_RPC("Sending Ping RTS PDU"); + + s = stream_new(header.frag_length); + rts_pdu_header_write(s, &header); /* RTS Header (20 bytes) */ + stream_seal(s); + + rpc_in_write(rpc, s->data, s->size); + + stream_free(s); + + return true; +} + +int rts_recv_pdu_commands(rdpRpc* rpc, RTS_PDU* rts_pdu) +{ + int i; + STREAM* s; + uint32 CommandType; + + s = stream_new(0); + stream_attach(s, rts_pdu->content, rts_pdu->header.frag_length); + + DEBUG_RTS("numberOfCommands:%d", rts_pdu->header.numberOfCommands); + + if (rts_pdu->header.flags & RTS_FLAG_PING) { - rpc_send_keep_alive_pdu(rpc); + rts_send_keep_alive_pdu(rpc); return 0; } - for (i = 0; i < header.numberOfCommands; i++) + for (i = 0; i < rts_pdu->header.numberOfCommands; i++) { stream_read_uint32(s, CommandType); /* CommandType (4 bytes) */ @@ -363,5 +649,61 @@ int rts_pdu_recv(rdpRpc* rpc, STREAM* s) } } + stream_detach(s); + stream_free(s); + return 0; } + +int rts_recv_pdu(rdpRpc* rpc, RTS_PDU* rts_pdu) +{ + STREAM* s; + int status; + int length; + uint8 header_buffer[20]; + rdpTls* tls_out = rpc->tls_out; + + /* read first 20 bytes to get RTS PDU Header */ + status = tls_read(tls_out, (uint8*) &header_buffer, 20); + + if (status <= 0) + { + printf("rts_recv error\n"); + return status; + } + + s = stream_new(0); + stream_attach(s, header_buffer, 20); + + rts_pdu_header_read(s, &(rts_pdu->header)); + + stream_detach(s); + stream_free(s); + + length = rts_pdu->header.frag_length - 20; + rts_pdu->content = (uint8*) xmalloc(length); + + status = tls_read(tls_out, rts_pdu->content, length); + + if (status < 0) + { + printf("rts_recv error\n"); + return status; + } + + if (rts_pdu->header.ptype != PTYPE_RTS) + { + printf("rts_recv error: unexpected ptype:%d\n", rts_pdu->header.ptype); + return -1; + } + +#ifdef WITH_DEBUG_RTS + printf("rts_recv(): length: %d\n", length); + freerdp_hexdump(rts_pdu->content, length); + printf("\n"); +#endif + + rts_recv_pdu_commands(rpc, rts_pdu); + + return rts_pdu->header.frag_length; +} diff --git a/libfreerdp-core/rts.h b/libfreerdp-core/rts.h index ecd24344b..b7ce7ae2b 100644 --- a/libfreerdp-core/rts.h +++ b/libfreerdp-core/rts.h @@ -104,6 +104,15 @@ struct _rts_pdu_header }; typedef struct _rts_pdu_header RTS_PDU_HEADER; +struct _rts_pdu +{ + RTS_PDU_HEADER header; + uint8* content; +}; +typedef struct _rts_pdu RTS_PDU; + +boolean rts_connect(rdpRpc* rpc); + void rts_pdu_header_read(STREAM* s, RTS_PDU_HEADER* header); void rts_pdu_header_write(STREAM* s, RTS_PDU_HEADER* header); @@ -138,7 +147,13 @@ void rts_destination_command_write(STREAM* s, uint32 Destination); void rts_ping_traffic_sent_notify_command_read(rdpRpc* rpc, STREAM* s); void rts_ping_traffic_sent_notify_command_write(STREAM* s, uint32 PingTrafficSent); -int rts_pdu_recv(rdpRpc* rpc, STREAM* s); +boolean rts_send_CONN_A1_pdu(rdpRpc* rpc); +boolean rts_send_CONN_B1_pdu(rdpRpc* rpc); +boolean rts_send_keep_alive_pdu(rdpRpc* rpc); +boolean rts_send_flow_control_ack_pdu(rdpRpc* rpc); +boolean rts_send_ping_pdu(rdpRpc* rpc); + +int rts_recv_pdu(rdpRpc* rpc, RTS_PDU* rts_pdu); #ifdef WITH_DEBUG_TSG #define WITH_DEBUG_RTS