diff options
author | Jay Sorg <jay.sorg@gmail.com> | 2014-07-10 03:25:50 +0400 |
---|---|---|
committer | Jay Sorg <jay.sorg@gmail.com> | 2014-07-10 03:25:50 +0400 |
commit | 1fb5636af2c910b6b03455fc37d4bd5d4d20a582 (patch) | |
tree | aa40f54c7fb21633bde8f06351a7e95a8656064d /libfreerdp-core | |
parent | 54317e97b85cd925f20849539d8a67b6812a293d (diff) |
ts gateway support
Diffstat (limited to 'libfreerdp-core')
-rw-r--r-- | libfreerdp-core/rpch.c | 1654 | ||||
-rw-r--r-- | libfreerdp-core/rpch.h | 630 | ||||
-rw-r--r-- | libfreerdp-core/tsg.c | 309 | ||||
-rw-r--r-- | libfreerdp-core/tsg.h | 59 |
4 files changed, 2652 insertions, 0 deletions
diff --git a/libfreerdp-core/rpch.c b/libfreerdp-core/rpch.c new file mode 100644 index 0000000..6c060e2 --- /dev/null +++ b/libfreerdp-core/rpch.c @@ -0,0 +1,1654 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * RDP Security + * + * Copyright 2012 Fujitsu Technology Solutions GmbH - + * Dmitrij Jasnov <dmitrij.jasnov@ts.fujitsu.com> + * Copyright 2014 Jay Sorg <jay.sorg@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * http://msdn.microsoft.com/en-us/library/cc243950.aspx + * + */ + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <openssl/rand.h> + +#include "rpch.h" + +#define HTTP_STREAM_SIZE 0xFFFF + +//#define WITH_DEBUG_RPCH 1 + +#define LLOG_LEVEL 1 +#define LLOGLN(_level, _args) \ + do { if (_level < LLOG_LEVEL) { printf _args ; printf("\n"); } } while (0) + +rdpRpch* rpch_new(rdpSettings* settings) +{ + rdpRpch* rpch; + rpch = (rdpRpch*) xzalloc(sizeof(rdpRpch)); + + rpch->http_in = (rdpRpchHTTP*) xzalloc(sizeof(rdpRpchHTTP)); + rpch->http_out = (rdpRpchHTTP*) xzalloc(sizeof(rdpRpchHTTP)); + + rpch->http_in->ntht = ntlmssp_new(); + rpch->http_out->ntht = ntlmssp_new(); + rpch->http_in->state = RPCH_HTTP_DISCONNECTED; + rpch->http_out->state = RPCH_HTTP_DISCONNECTED; + + rpch->read_buffer = NULL; + rpch->write_buffer = NULL; + rpch->read_buffer_len = 0; + rpch->write_buffer_len = 0; + + rpch->BytesReceived = 0; + rpch->AwailableWindow = 0; + rpch->BytesSent = 0; + rpch->RecAwailableWindow = 0; + + rpch->settings = settings; + + rpch->ntlmssp = ntlmssp_new(); + + rpch->call_id = 0; + return rpch; +} + +tbool rpch_attach(rdpRpch* rpch, rdpTcp* tcp_in, rdpTcp* tcp_out, rdpTls* tls_in, rdpTls* tls_out) +{ + rpch->tcp_in = tcp_in; + rpch->tcp_out = tcp_out; + rpch->tls_in = tls_in; + rpch->tls_out = tls_out; + + return true; +} + +int force_read(rdpTls* tls, uint8* data, int bytes) +{ + int total_read; + int read; + + LLOGLN(10, ("force_read:")); + total_read = 0; + while (total_read < bytes) + { + LLOGLN(10, ("force_read: reading %d bytes", bytes - total_read)); + read = tls_read(tls, data + total_read, bytes - total_read); + if (read < 0) + { + /* disconnect or other error */ + return -1; + } + if (read == 0) + { + LLOGLN(0, ("force_read: read == 0, sleeping and reading again")); + tcp_can_recv(tls->sockfd, 100); + continue; + } + total_read += read; + } + return total_read; +} + +STREAM * read_http(rdpTls* tls, int* acontent_length, tbool read_content) +{ + uint8 buf[1024]; + tbool done; + int bytes; + int offset; + int processed; + int content_length; + STREAM* s; + char mark[4]; + char cc; + char* find_text; + + mark[0] = 0; + mark[1] = 0; + mark[2] = 0; + mark[3] = 0; + s = stream_new(64 * 1024); + bytes = 0; + offset = 0; + processed = 0; + done = false; + while (!done) + { + if (bytes < 1) + { + offset = 0; + LLOGLN(0, ("read_http: reading 1024")); + bytes = tls_read(tls, buf, 1024); + if (bytes < 0) + { + LLOGLN(0, ("read_http: tls_read failed")); + stream_free(s); + return NULL; + } + if (bytes == 0) + { + tcp_can_recv(tls->sockfd, 100); + continue; + } + } + cc = buf[offset]; + processed++; + offset++; + bytes--; + mark[0] = mark[1]; + mark[1] = mark[2]; + mark[2] = mark[3]; + mark[3] = cc; + stream_write_uint8(s, cc); + if ((mark[2] == '\n') && (mark[3] == '\n')) + { + done = true; + continue; + } + if ((mark[0] == '\r') && (mark[1] == '\n') && + (mark[2] == '\r') && (mark[3] == '\n')) + { + done = true; + continue; + } + } + + LLOGLN(0, ("read_http: bytes %d offset %d", bytes, offset)); + tls_return(tls, buf + offset, bytes); + + find_text = strstr((char*)(s->data), "Content-Length:"); + if (find_text == NULL) + { + LLOGLN(0, ("read_http: can not fine 'Content-Length:' in http")); + stream_free(s); + return NULL; + } + offset = 15; + while (find_text[offset] == ' ') + { + offset++; + } + content_length = atoi(find_text + offset); + if (acontent_length != NULL) + { + *acontent_length = content_length; + } + if (content_length > 0 && read_content) + { + while (content_length > 0) + { + bytes = tls_read(tls, s->p, content_length); + if (bytes < 0) + { + LLOGLN(0, ("read_http: tls_read failed")); + stream_free(s); + return NULL; + } + if (bytes == 0) + { + tcp_can_recv(tls->sockfd, 100); + continue; + } + s->p += bytes; + content_length -= bytes; + } + } + + return s; +} + +tbool rpch_out_connect_http(rdpRpch* rpch) +{ + rdpTls* tls_out = rpch->tls_out; + rdpSettings* settings = rpch->settings; + rdpRpchHTTP* http_out = rpch->http_out; + NTLMSSP* http_out_ntlmssp = http_out->ntht; + + STREAM* ntlmssp_stream; + STREAM* http_stream; + + int decoded_ntht_length; + int encoded_ntht_length = 0; + int bytes; + + char* ntlm_text; + + LLOGLN(10, ("rpch_out_connect_http:")); + + uint8* decoded_ntht_data; + uint8* encoded_ntht_data = NULL; + + ntlmssp_stream = stream_new(0xFFFF); + http_stream = stream_new(0xFFFF); + + ntlmssp_set_username(http_out_ntlmssp, settings->tsg_username); + ntlmssp_set_password(http_out_ntlmssp, settings->tsg_password); + ntlmssp_set_domain(http_out_ntlmssp, settings->tsg_domain); + ntlmssp_set_workstation(http_out_ntlmssp, "WORKSTATION"); /* TODO insert proper w.name */ + + LLOGLN(10, ("rpch_out_connect_http: tsg_username %s tsg_password %s tsg_domain %s", + settings->tsg_username, settings->tsg_password, settings->tsg_domain)); + + ntlmssp_send(http_out_ntlmssp, ntlmssp_stream); + + decoded_ntht_length = (int) (ntlmssp_stream->p - ntlmssp_stream->data); + decoded_ntht_data = (uint8*) xmalloc(decoded_ntht_length); + + ntlmssp_stream->p = ntlmssp_stream->data; + stream_read(ntlmssp_stream, decoded_ntht_data, decoded_ntht_length); + + stream_clear(ntlmssp_stream); + ntlmssp_stream->p = ntlmssp_stream->data; + + crypto_base64_encode(decoded_ntht_data, decoded_ntht_length, &encoded_ntht_data, &encoded_ntht_length); + + stream_write(http_stream, "RPC_OUT_DATA /rpc/rpcproxy.dll?localhost:3388 HTTP/1.1\n", 55); + stream_write(http_stream, "Accept: application/rpc\n", 24); + stream_write(http_stream, "Cache-Control: no-cache\n", 24); + stream_write(http_stream, "Connection: Keep-Alive\n", 23); + stream_write(http_stream, "Content-Length: 0\n", 18); + stream_write(http_stream, "User-Agent: MSRPC\n", 18); + stream_write(http_stream, "Host: ", 6); + stream_write(http_stream, settings->tsg_server, strlen(settings->tsg_server)); + stream_write(http_stream, "\n", 1); + stream_write(http_stream, "Pragma: ResourceTypeUuid=44e265dd-7daf-42cd-8560-3cdb6e7a2729, SessionId=33ad20ac-7469-4f63-946d-113eac21a23c\n", 110); + stream_write(http_stream, "Authorization: NTLM ", 20); + stream_write(http_stream, encoded_ntht_data, encoded_ntht_length); + stream_write(http_stream, "\n\n", 2); + + LLOGLN(10, ("rpch_out_connect_http: sending\n%s", http_stream->data)); + + DEBUG_RPCH("\nSend:\n%s\n", http_stream->data); + + bytes = (int) (http_stream->p - http_stream->data); + tls_write(tls_out, http_stream->data, bytes); + stream_clear(http_stream); + http_stream->p = http_stream->data; + + xfree(decoded_ntht_data); + + encoded_ntht_length = -1; + xfree(encoded_ntht_data); + encoded_ntht_data = NULL; + http_out->contentLength = 0; + + LLOGLN(10, ("rpch_out_connect_http: 1")); + + stream_free(http_stream); + http_stream = read_http(tls_out, NULL, true); + + if (http_stream == NULL) + { + LLOGLN(0, ("rpch_out_connect_http: error http_stream is nil")); + return false; + } + + ntlm_text = strstr((char*)(http_stream->data), "NTLM "); + if (ntlm_text != NULL) + { + encoded_ntht_data = (uint8*)(ntlm_text + 5); + encoded_ntht_length = 0; + while (encoded_ntht_data[encoded_ntht_length] != '\r' && + encoded_ntht_data[encoded_ntht_length] != '\n') + { + encoded_ntht_length++; + } + } + + LLOGLN(0, ("rpch_out_connect_http: encoded_ntht_length %d encoded_ntht_data %s", + encoded_ntht_length, encoded_ntht_data)); + + if (encoded_ntht_length < 1) /* No NTLM data was found */ + { + LLOGLN(0, ("rpch_out_connect_http: error encoded_ntht_length < 1")); + return false; + } + + http_stream->p = http_stream->data; + + crypto_base64_decode(encoded_ntht_data, encoded_ntht_length, + &decoded_ntht_data, &decoded_ntht_length); + + stream_write(ntlmssp_stream, decoded_ntht_data, decoded_ntht_length); + ntlmssp_stream->p = ntlmssp_stream->data; + + xfree(decoded_ntht_data); + + ntlmssp_recv(http_out_ntlmssp, ntlmssp_stream); + stream_clear(ntlmssp_stream); + ntlmssp_stream->p = ntlmssp_stream->data; + + ntlmssp_send(http_out_ntlmssp, ntlmssp_stream); + + decoded_ntht_length = (int) (ntlmssp_stream->p - ntlmssp_stream->data); + decoded_ntht_data = (uint8*) xmalloc(decoded_ntht_length); + ntlmssp_stream->p = ntlmssp_stream->data; + stream_read(ntlmssp_stream, decoded_ntht_data, decoded_ntht_length); + + stream_clear(ntlmssp_stream); + ntlmssp_stream->p = ntlmssp_stream->data; + + crypto_base64_encode(decoded_ntht_data, decoded_ntht_length, + &encoded_ntht_data, &encoded_ntht_length); + + stream_write(http_stream, "RPC_OUT_DATA /rpc/rpcproxy.dll?localhost:3388 HTTP/1.1\n", 55); + stream_write(http_stream, "Accept: application/rpc\n", 24); + stream_write(http_stream, "Cache-Control: no-cache\n", 24); + stream_write(http_stream, "Connection: Keep-Alive\n", 23); + stream_write(http_stream, "Content-Length: 76\n", 19); + stream_write(http_stream, "User-Agent: MSRPC\n", 18); + stream_write(http_stream, "Host: ", 6); + stream_write(http_stream, settings->tsg_server, strlen(settings->tsg_server)); + stream_write(http_stream, "\n", 1); + stream_write(http_stream, "Pragma: ResourceTypeUuid=44e265dd-7daf-42cd-8560-3cdb6e7a2729, SessionId=33ad20ac-7469-4f63-946d-113eac21a23c\n", 110); + stream_write(http_stream, "Authorization: NTLM ", 20); + stream_write(http_stream, encoded_ntht_data, encoded_ntht_length); + stream_write(http_stream, "\n\n", 2); + + http_out->contentLength = 76; + http_out->remContentLength = 76; + + DEBUG_RPCH("\nSend:\n%s\n", http_stream->data); + + tls_write(tls_out, http_stream->data, http_stream->p - http_stream->data); + + stream_clear(http_stream); + http_stream->p = http_stream->data; + + xfree(decoded_ntht_data); + xfree(encoded_ntht_data); + /* At this point OUT connection is ready to send CONN/A1 and start with recieving data */ + + http_out->state = RPCH_HTTP_SENDING; + + LLOGLN(10, ("rpch_out_connect_http: out")); + + return true; +} + +tbool rpch_in_connect_http(rdpRpch* rpch) +{ + rdpTls* tls_in = rpch->tls_in; + rdpSettings* settings = rpch->settings; + rdpRpchHTTP* http_in = rpch->http_in; + NTLMSSP* http_in_ntlmssp = http_in->ntht; + + STREAM* ntlmssp_stream; + STREAM* http_stream; + + int decoded_ntht_length; + int encoded_ntht_length = 0; + int bytes; + + uint8* decoded_ntht_data; + uint8* encoded_ntht_data = NULL; + + char* ntlm_text; + + LLOGLN(10, ("rpch_in_connect_http:")); + + ntlmssp_stream = stream_new(0xFFFF); + http_stream = stream_new(0xFFFF); + + ntlmssp_set_username(http_in_ntlmssp, settings->tsg_username); + ntlmssp_set_password(http_in_ntlmssp, settings->tsg_password); + ntlmssp_set_domain(http_in_ntlmssp, settings->tsg_domain); + ntlmssp_set_workstation(http_in_ntlmssp, "WORKSTATION"); /* TODO insert proper w.name */ + + LLOGLN(10, ("rpch_in_connect_http: tsg_username %s tsg_password %s tsg_domain %s", + settings->tsg_username, settings->tsg_password, settings->tsg_domain)); + + ntlmssp_send(http_in_ntlmssp, ntlmssp_stream); + + decoded_ntht_length = (int) (ntlmssp_stream->p - ntlmssp_stream->data); + decoded_ntht_data = (uint8*) xmalloc(decoded_ntht_length); + + ntlmssp_stream->p = ntlmssp_stream->data; + stream_read(ntlmssp_stream, decoded_ntht_data, decoded_ntht_length); + + stream_clear(ntlmssp_stream); + ntlmssp_stream->p = ntlmssp_stream->data; + + crypto_base64_encode(decoded_ntht_data, decoded_ntht_length, &encoded_ntht_data, &encoded_ntht_length); + + stream_write(http_stream, "RPC_IN_DATA /rpc/rpcproxy.dll?localhost:3388 HTTP/1.1\n", 54); + stream_write(http_stream, "Accept: application/rpc\n", 24); + stream_write(http_stream, "Cache-Control: no-cache\n", 24); + stream_write(http_stream, "Connection: Keep-Alive\n", 23); + stream_write(http_stream, "Content-Length: 0\n", 18); + stream_write(http_stream, "User-Agent: MSRPC\n", 18); + stream_write(http_stream, "Host: ", 6); + stream_write(http_stream, settings->tsg_server, strlen(settings->tsg_server)); + stream_write(http_stream, "\n", 1); + stream_write(http_stream, "Pragma: ResourceTypeUuid=44e265dd-7daf-42cd-8560-3cdb6e7a2729, SessionId=33ad20ac-7469-4f63-946d-113eac21a23c\n", 110); + stream_write(http_stream, "Authorization: NTLM ", 20); + stream_write(http_stream, encoded_ntht_data, encoded_ntht_length); + stream_write(http_stream, "\n\n", 2); + + LLOGLN(10, ("rpch_in_connect_http: sending\n%s", http_stream->data)); + + DEBUG_RPCH("\nSend:\n%s\n", http_stream->data); + + bytes = (int) (http_stream->p - http_stream->data); + tls_write(tls_in, http_stream->data, bytes); + stream_clear(http_stream); + http_stream->p = http_stream->data; + + xfree(decoded_ntht_data); + + encoded_ntht_length = -1; + xfree(encoded_ntht_data); + encoded_ntht_data = NULL; + http_in->contentLength = 0; + + LLOGLN(10, ("rpch_in_connect_http: 1")); + + stream_free(http_stream); + http_stream = read_http(tls_in, NULL, true); + + if (http_stream == NULL) + { + LLOGLN(0, ("rpch_in_connect_http: error http_stream is nil")); + return false; + } + + ntlm_text = strstr((char*)(http_stream->data), "NTLM "); + if (ntlm_text != NULL) + { + encoded_ntht_data = (uint8*)(ntlm_text + 5); + encoded_ntht_length = 0; + while (encoded_ntht_data[encoded_ntht_length] != '\r' && + encoded_ntht_data[encoded_ntht_length] != '\n') + { + encoded_ntht_length++; + } + } + + LLOGLN(0, ("rpch_in_connect_http: encoded_ntht_length %d encoded_ntht_data %s", + encoded_ntht_length, encoded_ntht_data)); + + + if (encoded_ntht_length < 1) /* No NTLM data was found */ + { + LLOGLN(0, ("rpch_in_connect_http: error encoded_ntht_length < 1")); + return false; + } + + http_stream->p = http_stream->data; + + crypto_base64_decode(encoded_ntht_data, encoded_ntht_length, + &decoded_ntht_data, &decoded_ntht_length); + + stream_write(ntlmssp_stream, decoded_ntht_data, decoded_ntht_length); + ntlmssp_stream->p = ntlmssp_stream->data; + + xfree(decoded_ntht_data); + + ntlmssp_recv(http_in_ntlmssp, ntlmssp_stream); + stream_clear(ntlmssp_stream); + ntlmssp_stream->p = ntlmssp_stream->data; + + ntlmssp_send(http_in_ntlmssp, ntlmssp_stream); + + decoded_ntht_length = (int) (ntlmssp_stream->p - ntlmssp_stream->data); + decoded_ntht_data = (uint8*) xmalloc(decoded_ntht_length); + ntlmssp_stream->p = ntlmssp_stream->data; + stream_read(ntlmssp_stream, decoded_ntht_data, decoded_ntht_length); + + stream_clear(ntlmssp_stream); + ntlmssp_stream->p = ntlmssp_stream->data; + + crypto_base64_encode(decoded_ntht_data, decoded_ntht_length, &encoded_ntht_data, &encoded_ntht_length); + + stream_write(http_stream, "RPC_IN_DATA /rpc/rpcproxy.dll?localhost:3388 HTTP/1.1\n", 54); + stream_write(http_stream, "Accept: application/rpc\n", 24); + stream_write(http_stream, "Cache-Control: no-cache\n", 24); + stream_write(http_stream, "Connection: Keep-Alive\n", 23); + stream_write(http_stream, "Content-Length: 1073741824\n", 27); + stream_write(http_stream, "User-Agent: MSRPC\n", 18); + stream_write(http_stream, "Host: ", 6); + stream_write(http_stream, settings->tsg_server, strlen(settings->tsg_server)); + stream_write(http_stream, "\n", 1); + stream_write(http_stream, "Pragma: ResourceTypeUuid=44e265dd-7daf-42cd-8560-3cdb6e7a2729, SessionId=33ad20ac-7469-4f63-946d-113eac21a23c\n", 110); + stream_write(http_stream, "Authorization: NTLM ", 20); + stream_write(http_stream, encoded_ntht_data, encoded_ntht_length); + stream_write(http_stream, "\n\n", 2); + + http_in->contentLength = 1073741824; + http_in->remContentLength = 1073741824; + + DEBUG_RPCH("\nSend:\n%s\n", http_stream->data); + + tls_write(tls_in, http_stream->data, http_stream->p - http_stream->data); + + stream_clear(http_stream); + http_stream->p = http_stream->data; + + xfree(decoded_ntht_data); + xfree(encoded_ntht_data); + /* At this point IN connection is ready to send CONN/B1 and start with sending data */ + + http_in->state = RPCH_HTTP_SENDING; + + LLOGLN(10, ("rpch_in_connect_http: out")); + + return true; +} + +int rpch_out_write(rdpRpch* rpch, uint8* data, int length) +{ + rdpRpchHTTP* http_out = rpch->http_out; + rdpTls* tls_out = rpch->tls_out; + int status = -1; + int sent = 0; + + LLOGLN(10, ("rpch_out_write: in length %d", length)); + + if (http_out->remContentLength < length) + { + LLOGLN(0, ("rpch_out_write: RPCH Error: HTTP frame is over.")); + return -1;/* TODO ChannelRecycling */ + } + +#ifdef WITH_DEBUG_RPCH + printf("rpch_out_write(): length: %d\n", length); + freerdp_hexdump(data, length); + printf("\n"); +#endif + while (sent < length) + { + status = tls_write(tls_out, data + sent, length - sent); + + if (status <= 0) + { + LLOGLN(0, ("rpch_out_write: error")); + return status; /* TODO no idea how to handle errors */ + } + + sent += status; + } + + http_out->remContentLength -= sent; + + LLOGLN(10, ("rpch_out_write: out sent %d", sent)); + + return sent; +} + +int rpch_in_write(rdpRpch* rpch, uint8* data, int length) +{ + rdpRpchHTTP* http_in = rpch->http_in; + rdpTls* tls_in = rpch->tls_in; + int status = -1; + int sent = 0; + + LLOGLN(10, ("rpch_in_write:")); + + if (http_in->remContentLength < length) + { + printf("RPCH Error: HTTP frame is over.\n"); + return -1;/* TODO ChannelRecycling */ + } +#ifdef WITH_DEBUG_RPCH + printf("\nrpch_in_send(): length: %d, remaining content length: %d\n", length, http_in->remContentLength); + freerdp_hexdump(data, length); + printf("\n"); +#endif + while (sent < length) + { + status = tls_write(tls_in, data+sent, length-sent); + + if (status <= 0) + return status;/* TODO no idea how to handle errors */ + + sent += status; + } + + rpch->BytesSent += sent; + http_in->remContentLength -= sent; + + return sent; +} + +uint8* rpch_create_cookie() +{ + uint8 *ret = xmalloc(16); + RAND_pseudo_bytes(ret, 16); + return ret; +} + +tbool rpch_out_send_CONN_A1(rdpRpch* rpch) +{ + STREAM* pdu = stream_new(76); + + uint8 rpc_vers = 0x05; + uint8 rpc_vers_minor = 0x00; + uint8 ptype = PTYPE_RTS; + uint8 pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG; + uint32 packet_drep = 0x00000010; + uint16 frag_length = 76; + uint16 auth_length = 0; + uint32 call_id = 0x00000000; + uint16 flags = 0x0000; + uint16 num_commands = 0x0004; + + /* Version */ + uint32 vCommandType = 0x00000006; + uint32 Version = 0x00000001; + + /* VirtualConnectionCookie */ + uint32 vccCommandType = 0x00000003; + + LLOGLN(10, ("rpch_out_send_CONN_A1:")); + + rpch->virtualConnectionCookie = rpch_create_cookie(); /* 16 bytes */ + + LLOGLN(10, ("rpch_out_send_CONN_A1: 1")); + + /* OUTChannelCookie */ + uint32 occCommandType = 0x00000003; + rpch->OUTChannelCookie = rpch_create_cookie(); /* 16 bytes */ + + LLOGLN(10, ("rpch_out_send_CONN_A1: 2")); + + /* ReceiveWindowSize */ + uint32 rwsCommandType = 0x00000000; + uint32 reseiveWindowSize = 0x00010000; + rpch->AwailableWindow = reseiveWindowSize; + + stream_write_uint8(pdu, rpc_vers); + stream_write_uint8(pdu, rpc_vers_minor); + stream_write_uint8(pdu, ptype); + stream_write_uint8(pdu, pfc_flags); + stream_write_uint32(pdu, packet_drep); + stream_write_uint16(pdu, frag_length); + stream_write_uint16(pdu, auth_length); + stream_write_uint32(pdu, call_id); + stream_write_uint16(pdu, flags); + stream_write_uint16(pdu, num_commands); + stream_write_uint32(pdu, vCommandType); + stream_write_uint32(pdu, Version); + stream_write_uint32(pdu, vccCommandType); + stream_write(pdu, rpch->virtualConnectionCookie, 16); + stream_write_uint32(pdu, occCommandType); + stream_write(pdu, rpch->OUTChannelCookie, 16); + stream_write_uint32(pdu, rwsCommandType); + stream_write_uint32(pdu, reseiveWindowSize); + + if (!rpch_out_write(rpch, pdu->data, pdu->p - pdu->data)) + { + LLOGLN(0, ("rpch_out_send_CONN_A1: rpch_out_write failed")); + stream_free(pdu); + return false; + } + + stream_free(pdu); + + LLOGLN(10, ("rpch_out_send_CONN_A1: out")); + return true; +} + +tbool rpch_in_send_CONN_B1(rdpRpch* rpch) +{ + STREAM* pdu = stream_new(104); + + uint8 rpc_vers = 0x05; + uint8 rpc_vers_minor = 0x00; + uint8 ptype = PTYPE_RTS; + uint8 pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG; + uint32 packet_drep = 0x00000010; + uint16 frag_length = 104; + uint16 auth_length = 0; + uint32 call_id = 0x00000000; + uint16 flags = 0x0000; + uint16 num_commands = 0x0006; + + /* Version */ + uint32 vCommandType = 0x00000006; + uint32 Version = 0x00000001; + + /* VirtualConnectionCookie */ + uint32 vccCommandType = 0x00000003; + + /* INChannelCookie */ + uint32 iccCommandType = 0x00000003; + rpch->INChannelCookie = rpch_create_cookie(); /* 16bytes */ + + /* ChannelLifetime */ + uint32 clCommandType = 0x00000004; + uint32 ChannelLifetime = 0x40000000; + + /* ClientKeepalive */ + uint32 ckCommandType = 0x00000005; + uint32 ClientKeepalive = 0x000493e0; + + /* AssociationGroupId */ + uint32 agidCommandType = 0x0000000c; + uint8* AssociationGroupId = rpch_create_cookie(); /* 16bytes */ + + LLOGLN(10, ("rpch_in_send_CONN_B1:")); + + stream_write_uint8(pdu, rpc_vers); + stream_write_uint8(pdu, rpc_vers_minor); + stream_write_uint8(pdu, ptype); + stream_write_uint8(pdu, pfc_flags); + stream_write_uint32(pdu, packet_drep); + stream_write_uint16(pdu, frag_length); + stream_write_uint16(pdu, auth_length); + stream_write_uint32(pdu, call_id); + stream_write_uint16(pdu, flags); + stream_write_uint16(pdu, num_commands); + stream_write_uint32(pdu, vCommandType); + stream_write_uint32(pdu, Version); + stream_write_uint32(pdu, vccCommandType); + stream_write(pdu, rpch->virtualConnectionCookie, 16); + stream_write_uint32(pdu, iccCommandType); + stream_write(pdu, rpch->INChannelCookie, 16); + stream_write_uint32(pdu, clCommandType); + stream_write_uint32(pdu, ChannelLifetime); + stream_write_uint32(pdu, ckCommandType); + stream_write_uint32(pdu, ClientKeepalive); + stream_write_uint32(pdu, agidCommandType); + stream_write(pdu, AssociationGroupId, 16); + + rpch_in_write(rpch, pdu->data, pdu->p - pdu->data); + + stream_free(pdu); + LLOGLN(10, ("rpch_in_send_CONN_B1: out")); + + return true; +} + +tbool rpch_in_send_keep_alive(rdpRpch* rpch) +{ + STREAM* pdu = stream_new(28); + + uint8 rpc_vers = 0x05; + uint8 rpc_vers_minor = 0x00; + uint8 ptype = PTYPE_RTS; + uint8 pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG; + uint32 packet_drep = 0x00000010; + uint16 frag_length = 28; + uint16 auth_length = 0; + uint32 call_id = 0x00000000; + uint16 flags = 0x0002; + uint16 num_commands = 0x0001; + + /* ClientKeepalive */ + uint32 ckCommandType = 0x00000005; + uint32 ClientKeepalive = 0x00007530; + + LLOGLN(10, ("rpch_in_send_keep_alive:")); + stream_write_uint8(pdu, rpc_vers); + stream_write_uint8(pdu, rpc_vers_minor); + stream_write_uint8(pdu, ptype); + stream_write_uint8(pdu, pfc_flags); + stream_write_uint32(pdu, packet_drep); + stream_write_uint16(pdu, frag_length); + stream_write_uint16(pdu, auth_length); + stream_write_uint32(pdu, call_id); + stream_write_uint16(pdu, flags); + stream_write_uint16(pdu, num_commands); + stream_write_uint32(pdu, ckCommandType); + stream_write_uint32(pdu, ClientKeepalive); + + rpch_in_write(rpch, pdu->data, pdu->p - pdu->data); + + stream_free(pdu); + + return true; +} + +tbool rpch_in_send_bind(rdpRpch* rpch) +{ + STREAM* ntlm_stream = stream_new(0xFFFF); + STREAM* pdu; + int bytes; + rpcconn_bind_hdr_t* bind_pdu; + + LLOGLN(10, ("rpch_in_send_bind:")); + + rpch->ntlmssp = ntlmssp_new(); + rpch->ntlmssp->ntlm_v2 = true; + ntlmssp_set_username(rpch->ntlmssp, rpch->settings->tsg_username); + ntlmssp_set_password(rpch->ntlmssp, rpch->settings->tsg_password); + ntlmssp_set_domain(rpch->ntlmssp, rpch->settings->tsg_domain); + ntlmssp_set_workstation(rpch->ntlmssp, "WORKSTATION"); /* TODO insert proper w.name */ + + ntlmssp_send(rpch->ntlmssp, ntlm_stream); + + bind_pdu = (rpcconn_bind_hdr_t*) xmalloc(sizeof(rpcconn_bind_hdr_t)); + bind_pdu->rpc_vers = 5; + bind_pdu->rpc_vers_minor = 0; + bind_pdu->PTYPE = PTYPE_BIND; + bind_pdu->pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG | PFC_PENDING_CANCEL | PFC_CONC_MPX; + bind_pdu->packed_drep[0] = 0x10; + bind_pdu->packed_drep[1] = 0x00; + bind_pdu->packed_drep[2] = 0x00; + bind_pdu->packed_drep[3] = 0x00; + bytes = (int) (ntlm_stream->p - ntlm_stream->data); + bind_pdu->frag_length = 124 + bytes; + bind_pdu->auth_length = bytes; + bind_pdu->call_id = 2; + bind_pdu->max_xmit_frag = 0x0FF8; + bind_pdu->max_recv_frag = 0x0FF8; + bind_pdu->assoc_group_id = 0; + bind_pdu->p_context_elem.n_context_elem = 2; + bind_pdu->p_context_elem.reserved = 0; + bind_pdu->p_context_elem.reserved2 = 0; + bind_pdu->p_context_elem.p_cont_elem = (p_cont_elem_t*) xmalloc(sizeof(p_cont_elem_t) * bind_pdu->p_context_elem.n_context_elem); + bind_pdu->p_context_elem.p_cont_elem[0].p_cont_id = 0; + bind_pdu->p_context_elem.p_cont_elem[0].n_transfer_syn = 1; + bind_pdu->p_context_elem.p_cont_elem[0].reserved = 0; + bind_pdu->p_context_elem.p_cont_elem[0].abstract_syntax.if_uuid.time_low = 0x44e265dd; + bind_pdu->p_context_elem.p_cont_elem[0].abstract_syntax.if_uuid.time_mid = 0x7daf; + bind_pdu->p_context_elem.p_cont_elem[0].abstract_syntax.if_uuid.time_hi_and_version = 0x42cd; + bind_pdu->p_context_elem.p_cont_elem[0].abstract_syntax.if_uuid.clock_seq_hi_and_reserved = 0x85; + bind_pdu->p_context_elem.p_cont_elem[0].abstract_syntax.if_uuid.clock_seq_low = 0x60; + bind_pdu->p_context_elem.p_cont_elem[0].abstract_syntax.if_uuid.node[0] = 0x3c; + bind_pdu->p_context_elem.p_cont_elem[0].abstract_syntax.if_uuid.node[1] = 0xdb; + bind_pdu->p_context_elem.p_cont_elem[0].abstract_syntax.if_uuid.node[2] = 0x6e; + bind_pdu->p_context_elem.p_cont_elem[0].abstract_syntax.if_uuid.node[3] = 0x7a; + bind_pdu->p_context_elem.p_cont_elem[0].abstract_syntax.if_uuid.node[4] = 0x27; + bind_pdu->p_context_elem.p_cont_elem[0].abstract_syntax.if_uuid.node[5] = 0x29; + bind_pdu->p_context_elem.p_cont_elem[0].abstract_syntax.if_version = 0x00030001; + bind_pdu->p_context_elem.p_cont_elem[0].transfer_syntaxes = xmalloc(sizeof(p_syntax_id_t)); + bind_pdu->p_context_elem.p_cont_elem[0].transfer_syntaxes[0].if_uuid.time_low = 0x8a885d04; + bind_pdu->p_context_elem.p_cont_elem[0].transfer_syntaxes[0].if_uuid.time_mid = 0x1ceb; + bind_pdu->p_context_elem.p_cont_elem[0].transfer_syntaxes[0].if_uuid.time_hi_and_version = 0x11c9; + bind_pdu->p_context_elem.p_cont_elem[0].transfer_syntaxes[0].if_uuid.clock_seq_hi_and_reserved = 0x9f; + bind_pdu->p_context_elem.p_cont_elem[0].transfer_syntaxes[0].if_uuid.clock_seq_low = 0xe8; + bind_pdu->p_context_elem.p_cont_elem[0].transfer_syntaxes[0].if_uuid.node[0] = 0x08; + bind_pdu->p_context_elem.p_cont_elem[0].transfer_syntaxes[0].if_uuid.node[1] = 0x00; + bind_pdu->p_context_elem.p_cont_elem[0].transfer_syntaxes[0].if_uuid.node[2] = 0x2b; + bind_pdu->p_context_elem.p_cont_elem[0].transfer_syntaxes[0].if_uuid.node[3] = 0x10; + bind_pdu->p_context_elem.p_cont_elem[0].transfer_syntaxes[0].if_uuid.node[4] = 0x48; + bind_pdu->p_context_elem.p_cont_elem[0].transfer_syntaxes[0].if_uuid.node[5] = 0x60; + bind_pdu->p_context_elem.p_cont_elem[0].transfer_syntaxes[0].if_version = 0x00000002; + bind_pdu->p_context_elem.p_cont_elem[1].p_cont_id = 1; + bind_pdu->p_context_elem.p_cont_elem[1].n_transfer_syn = 1; + bind_pdu->p_context_elem.p_cont_elem[1].reserved = 0; + bind_pdu->p_context_elem.p_cont_elem[1].abstract_syntax.if_uuid.time_low = 0x44e265dd; + bind_pdu->p_context_elem.p_cont_elem[1].abstract_syntax.if_uuid.time_mid = 0x7daf; + bind_pdu->p_context_elem.p_cont_elem[1].abstract_syntax.if_uuid.time_hi_and_version = 0x42cd; + bind_pdu->p_context_elem.p_cont_elem[1].abstract_syntax.if_uuid.clock_seq_hi_and_reserved = 0x85; + bind_pdu->p_context_elem.p_cont_elem[1].abstract_syntax.if_uuid.clock_seq_low = 0x60; + bind_pdu->p_context_elem.p_cont_elem[1].abstract_syntax.if_uuid.node[0] = 0x3c; + bind_pdu->p_context_elem.p_cont_elem[1].abstract_syntax.if_uuid.node[1] = 0xdb; + bind_pdu->p_context_elem.p_cont_elem[1].abstract_syntax.if_uuid.node[2] = 0x6e; + bind_pdu->p_context_elem.p_cont_elem[1].abstract_syntax.if_uuid.node[3] = 0x7a; + bind_pdu->p_context_elem.p_cont_elem[1].abstract_syntax.if_uuid.node[4] = 0x27; + bind_pdu->p_context_elem.p_cont_elem[1].abstract_syntax.if_uuid.node[5] = 0x29; + bind_pdu->p_context_elem.p_cont_elem[1].abstract_syntax.if_version = 0x00030001; + bind_pdu->p_context_elem.p_cont_elem[1].transfer_syntaxes = xmalloc(sizeof(p_syntax_id_t)); + bind_pdu->p_context_elem.p_cont_elem[1].transfer_syntaxes[0].if_uuid.time_low = 0x6cb71c2c; + bind_pdu->p_context_elem.p_cont_elem[1].transfer_syntaxes[0].if_uuid.time_mid = 0x9812; + bind_pdu->p_context_elem.p_cont_elem[1].transfer_syntaxes[0].if_uuid.time_hi_and_version = 0x4540; + bind_pdu->p_context_elem.p_cont_elem[1].transfer_syntaxes[0].if_uuid.clock_seq_hi_and_reserved = 0x03; + bind_pdu->p_context_elem.p_cont_elem[1].transfer_syntaxes[0].if_uuid.clock_seq_low = 0x00; + bind_pdu->p_context_elem.p_cont_elem[1].transfer_syntaxes[0].if_uuid.node[0] = 0x00; + bind_pdu->p_context_elem.p_cont_elem[1].transfer_syntaxes[0].if_uuid.node[1] = 0x00; + bind_pdu->p_context_elem.p_cont_elem[1].transfer_syntaxes[0].if_uuid.node[2] = 0x00; + bind_pdu->p_context_elem.p_cont_elem[1].transfer_syntaxes[0].if_uuid.node[3] = 0x00; + bind_pdu->p_context_elem.p_cont_elem[1].transfer_syntaxes[0].if_uuid.node[4] = 0x00; + bind_pdu->p_context_elem.p_cont_elem[1].transfer_syntaxes[0].if_uuid.node[5] = 0x00; + bind_pdu->p_context_elem.p_cont_elem[1].transfer_syntaxes[0].if_version = 0x00000001; + bind_pdu->auth_verifier.auth_pad = NULL; /* align(4); size_is(auth_pad_length) p*/ + bind_pdu->auth_verifier.auth_type = 0x0a; /* :01 which authent service */ + bind_pdu->auth_verifier.auth_level = 0x05; /* :01 which level within service */ + bind_pdu->auth_verifier.auth_pad_length = 0x00; /* :01 */ + bind_pdu->auth_verifier.auth_reserved = 0x00; /* :01 reserved, m.b.z. */ + bind_pdu->auth_verifier.auth_context_id = 0x00000000; /* :04 */ + bind_pdu->auth_verifier.auth_value = xmalloc(bind_pdu->auth_length); /* credentials; size_is(auth_length) p*/; + memcpy(bind_pdu->auth_verifier.auth_value, ntlm_stream->data, bind_pdu->auth_length); + + stream_free(ntlm_stream); + + pdu = stream_new(bind_pdu->frag_length); + + stream_write(pdu, bind_pdu, 24); + stream_write(pdu, &bind_pdu->p_context_elem, 4); + stream_write(pdu, bind_pdu->p_context_elem.p_cont_elem, 24); + stream_write(pdu, bind_pdu->p_context_elem.p_cont_elem[0].transfer_syntaxes, 20); + stream_write(pdu, bind_pdu->p_context_elem.p_cont_elem + 1, 24); + stream_write(pdu, bind_pdu->p_context_elem.p_cont_elem[1].transfer_syntaxes, 20); + if (bind_pdu->auth_verifier.auth_pad_length > 0) + { + stream_write(pdu, bind_pdu->auth_verifier.auth_pad, bind_pdu->auth_verifier.auth_pad_length); + } + stream_write(pdu, &bind_pdu->auth_verifier.auth_type, 8); /* assumed that uint8 pointer is 32bit long (4 bytes) */ + stream_write(pdu, bind_pdu->auth_verifier.auth_value, bind_pdu->auth_length); + + rpch_in_write(rpch, pdu->data, pdu->p - pdu->data); + /* TODO there are some alocatad memory */ + xfree(bind_pdu); + return true; +} + +tbool rpch_in_send_rpc_auth_3(rdpRpch* rpch) +{ + STREAM* ntlm_stream = stream_new(0xFFFF); + STREAM* pdu; + int bytes; + rpcconn_rpc_auth_3_hdr_t* rpc_auth_3_pdu; + + LLOGLN(10, ("rpch_in_send_rpc_auth_3:")); + ntlmssp_send(rpch->ntlmssp, ntlm_stream); + + rpc_auth_3_pdu = (rpcconn_rpc_auth_3_hdr_t*) xmalloc(sizeof(rpcconn_rpc_auth_3_hdr_t)); + rpc_auth_3_pdu->rpc_vers = 5; + rpc_auth_3_pdu->rpc_vers_minor = 0; + rpc_auth_3_pdu->PTYPE = PTYPE_RPC_AUTH_3; + rpc_auth_3_pdu->pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG | PFC_CONC_MPX; + rpc_auth_3_pdu->packed_drep[0] = 0x10; + rpc_auth_3_pdu->packed_drep[1] = 0x00; + rpc_auth_3_pdu->packed_drep[2] = 0x00; + rpc_auth_3_pdu->packed_drep[3] = 0x00; + bytes = (int) (ntlm_stream->p - ntlm_stream->data); + rpc_auth_3_pdu->frag_length = 28 + bytes; + rpc_auth_3_pdu->auth_length = bytes; + 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 */ + rpc_auth_3_pdu->auth_verifier.auth_type = 0x0a; /* :01 which authent service */ + rpc_auth_3_pdu->auth_verifier.auth_level = 0x05; /* :01 which level within service */ + rpc_auth_3_pdu->auth_verifier.auth_pad_length = 0x00; /* :01 */ + rpc_auth_3_pdu->auth_verifier.auth_reserved = 0x00; /* :01 reserved, m.b.z. */ + rpc_auth_3_pdu->auth_verifier.auth_context_id = 0x00000000; /* :04 */ + rpc_auth_3_pdu->auth_verifier.auth_value = xmalloc(rpc_auth_3_pdu->auth_length); /* credentials; size_is(auth_length) p */; + memcpy(rpc_auth_3_pdu->auth_verifier.auth_value, ntlm_stream->data, rpc_auth_3_pdu->auth_length); + + stream_free(ntlm_stream); + + pdu = stream_new(rpc_auth_3_pdu->frag_length); + + stream_write(pdu, rpc_auth_3_pdu, 20); + if (rpc_auth_3_pdu->auth_verifier.auth_pad_length > 0) + { + stream_write(pdu, rpc_auth_3_pdu->auth_verifier.auth_pad, rpc_auth_3_pdu->auth_verifier.auth_pad_length); + } + stream_write(pdu, &rpc_auth_3_pdu->auth_verifier.auth_type, 8); + stream_write(pdu, rpc_auth_3_pdu->auth_verifier.auth_value, rpc_auth_3_pdu->auth_length); + + rpch_in_write(rpch, pdu->data, pdu->p - pdu->data); + xfree(rpc_auth_3_pdu); + return true; +} + +/* +rpch_proceed_RTS: CommandType 0x00000001 +0000 01 00 00 00 46 80 00 00 00 00 01 00 3c c1 c2 bf ....F.......<... +0010 04 78 ad b2 d7 73 e1 49 e2 94 99 73 .x...s.I...s +* +* 0x08046 +* 0x100a6 +* 0x180c6 +*/ +static tbool rpch_in_send_flow_control(rdpRpch* rpch) +{ + int bytes; + STREAM* s; + uint8* fl_p; /* frag_length */ + uint8* holdp; + + LLOGLN(10, ("rpch_in_send_flow_control: BytesReceived 0x%8.8x", rpch->BytesReceived)); + s = stream_new(1024); + rpch->AwailableWindow = 0x00010000; + stream_write_uint8(s, 0x05); /* rpc_vers */ + stream_write_uint8(s, 0x00); /* rpc_vers_minor */ + stream_write_uint8(s, PTYPE_RTS); /* ptype */ + stream_write_uint8(s, PFC_FIRST_FRAG | PFC_LAST_FRAG); /* pfc_flags */ + stream_write_uint32(s, 0x00000010); /* packet_drep */ + fl_p = s->p; + stream_write_uint16(s, 0); /* frag_length, set later */ + stream_write_uint16(s, 0); /* auth_length */ + stream_write_uint32(s, 0x00000000); /* call_id */ + stream_write_uint16(s, 0x0002); /* flags */ + stream_write_uint16(s, 0x0002); /* num_commands */ + /* command 1 Destination */ + stream_write_uint32(s, 0x0000000d); /* ckCommandType */ + stream_write_uint32(s, 0x00000003); /* FDOutProxy */ + /* command 2 ClientKeepalive */ + stream_write_uint32(s, 0x00000001); /* ckCommandType */ + stream_write_uint32(s, rpch->BytesReceived); + stream_write_uint32(s, 0x00010000); + stream_write(s, rpch->OUTChannelCookie, 16); + bytes = (int) (s->p - s->data); + holdp = s->p; + s->p = fl_p; + stream_write_uint16(s, bytes); + s->p = holdp; + rpch_in_write(rpch, s->data, bytes); + stream_free(s); + return true; +} + +tbool rpch_in_send_ping(rdpRpch* rpch) +{ + STREAM* pdu = stream_new(20); + + uint8 rpc_vers = 0x05; + uint8 rpc_vers_minor = 0x00; + uint8 ptype = PTYPE_RTS; + uint8 pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG; + uint32 packet_drep = 0x00000010; + uint16 frag_length = 56; + uint16 auth_length = 0; + uint32 call_id = 0x00000000; + uint16 flags = 0x0001; + uint16 num_commands = 0x0000; + + stream_write_uint8(pdu, rpc_vers); + stream_write_uint8(pdu, rpc_vers_minor); + stream_write_uint8(pdu, ptype); + stream_write_uint8(pdu, pfc_flags); + stream_write_uint32(pdu, packet_drep); + stream_write_uint16(pdu, frag_length); + stream_write_uint16(pdu, auth_length); + stream_write_uint32(pdu, call_id); + stream_write_uint16(pdu, flags); + stream_write_uint16(pdu, num_commands); + + rpch_in_write(rpch, pdu->data, pdu->p - pdu->data); + + stream_free(pdu); + + return true; +} + +int rpch_out_read_http_header(rdpRpch* rpch) +{ + int status; + int content_length; + rdpTls* tls_out = rpch->tls_out; + rdpRpchHTTP* http_out = rpch->http_out; + STREAM* http_stream; + + http_stream = read_http(tls_out, &content_length, false); + if (http_stream == NULL) + { + LLOGLN(0, ("rpch_out_read_http_header: read_http failed")); + return -1; + } + + status = 0; + http_out->contentLength = content_length; + http_out->remContentLength = http_out->contentLength; + + DEBUG_RPCH("\nRecv HTTP header:\n%s", stream_get_head(http_stream)); + + stream_free(http_stream); + + return status; +} + +int rpch_proceed_RTS(rdpRpch* rpch, uint8* pdu, int length) +{ + uint16 flags = *(uint16*)(pdu + 16); + uint16 num_commands = *(uint16*)(pdu + 18); + uint8* iterator = pdu + 20; + uint32 CommandType; + int i; + + LLOGLN(10, ("rpch_proceed_RTS:")); + + if (flags & RTS_FLAG_PING) + { + LLOGLN(0, ("rpch_proceed_RTS: calling rpch_in_send_keep_alive")); + rpch_in_send_keep_alive(rpch); + return 0; + } + + for (i = 0; i < num_commands; i++) + { + CommandType = *(uint32*) iterator; + LLOGLN(10, ("rpch_proceed_RTS: CommandType 0x%8.8x", CommandType)); + switch (CommandType) + { + case 0x00000000: /* ReceiveWindowSize */ + iterator += 8; + break; + case 0x00000001: /* FlowControlAck */ + //freerdp_hexdump(iterator, 28); + iterator += 28; + break; + case 0x00000002: /* ConnectionTimeout */ + iterator += 8; + break; + case 0x00000003: /* Cookie */ + iterator += 20; + break; + case 0x00000004: /* ChannelLifetime */ + iterator += 8; + break; + case 0x00000005: /* ClientKeepalive */ + iterator += 8; + break; + case 0x00000006: /* Version */ + iterator += 8; + break; + case 0x00000007: /* Empty */ + iterator += 4; + break; + case 0x00000008: /* Padding */ + iterator += 8 + *(uint32*)(iterator + 4); + break; + case 0x00000009: /* NegativeANCE */ + iterator += 4; + break; + case 0x0000000a: /* ANCE */ + iterator += 4; + break; + case 0x0000000b: /* ClientAddress */ + iterator+= 4 + 4 + (12 * (*(uint32*)(iterator + 4))) + 12 ; + break; + case 0x0000000c: /* AssociationGroupId */ + iterator += 20; + break; + case 0x0000000d: /* Destination */ + //freerdp_hexdump(iterator, 8); + iterator += 8; + break; + case 0x0000000e: /* PingTrafficSentNotify */ + iterator += 8; + break; + default: + LLOGLN(0, ("rpch_proceed_RTS: Error: Unknown RTS CommandType: 0x%x", CommandType)); + return -1; + } + } + return 0; +} + +int rpch_out_read(rdpRpch* rpch, uint8* data, int length) +{ + int status; + rdpTls* tls_out = rpch->tls_out; + rdpRpchHTTP* http_out = rpch->http_out; + uint8* pdu; + uint8 ptype; + uint16 frag_length; + + LLOGLN(10, ("rpch_out_read:")); + +#if 0 + if (rpch->AwailableWindow < 0x00008FFF) /* Just a simple workaround */ + { + LLOGLN(10, ("rpch_out_read: calling rpch_in_send_flow_control")); + rpch_in_send_flow_control(rpch); /* send FlowControlAck every time AW reaches the half */ + } + + LLOGLN(10, ("rpch_out_read: 1")); + if (http_out->remContentLength <= 0xFFFF) /* TODO make ChannelRecycling */ + { + LLOGLN(10, ("rpch_out_read: calling rpch_out_read_http_header")); + if (rpch_out_read_http_header(rpch) < 0) + { + LLOGLN(0, ("rpch_out_read: rpch_out_read_http_header failed")); + return -1; + } + } + LLOGLN(10, ("rpch_out_read: 2")); + + if (http_out->contentLength != 0x40000000) + { + LLOGLN(0, ("rpch_out_read: 'Bad Request'")); + return -1; + } +#endif + + pdu = (uint8*) xmalloc(0xFFFF); + +#if 0 + status = force_read(tls_out, pdu, 1); + if (status != 0) + { + LLOGLN(0, ("rpch_out_read: error")); + return -1; + } + if (pdu[0] == 'H') + { + if (rpch_out_read_http_header(rpch) < 0) + { + LLOGLN(0, ("rpch_out_read: rpch_out_read_http_header failed")); + return -1; +> } + return 0; + } +#endif + + //status = tls_read(tls_out, pdu, 10); + status = force_read(tls_out, pdu, 10); + if (status <= 9) /* read first 10 bytes to get the frag_length value */ + { + LLOGLN(0, ("rpch_out_read: tls_read failed, not enough")); + xfree(pdu); + return -1; + } + + ptype = *(pdu + 2); + frag_length = *((uint16*)(pdu + 8)); + LLOGLN(10, ("rpch_out_read: frag_length %d", frag_length)); + + //status = tls_read(tls_out, pdu + 10, frag_length - 10); + status = force_read(tls_out, pdu + 10, frag_length - 10); + if (status < 0) + { + xfree(pdu); + return status; + } + + if (ptype == 0x14) /* RTS PDU */ + { + LLOGLN(0, ("rpch_out_read: RTS PDU received...")); + LLOGLN(10, ("rpch_out_read: calling rpch_proceed_RTS")); + rpch_proceed_RTS(rpch, pdu, frag_length); + xfree(pdu); + return 0; + } + else + { + rpch->BytesReceived += frag_length; /* RTS PDUs are not subjects for FlowControl */ + rpch->AwailableWindow -= frag_length; + } + + http_out->remContentLength -= frag_length; + + if (length < frag_length) + { + LLOGLN(10, ("rpch_out_read: length %d frag_length %d", length, frag_length)); + printf("rcph_out_read(): Error! Given buffer is to small. Recieved data fits not in.\n"); + xfree(pdu); + /* TODO add buffer for storing remaining data for the next read in + * case destination buffer is to small */ + return -1; + } + + memcpy(data, pdu, frag_length); + +#ifdef WITH_DEBUG_RPCH + printf("\nrpch_out_recv(): length: %d, remaining content length: %d\n", frag_length, http_out->remContentLength); + freerdp_hexdump(data, frag_length); + printf("\n"); +#endif + + xfree(pdu); + + return frag_length; +} + +int rpch_out_recv_bind_ack(rdpRpch* rpch) +{ + int pdu_length = 0x8FFF; /*32KB buffer*/ + uint8* pdu = xmalloc(pdu_length); + int status = rpch_out_read(rpch, pdu, pdu_length); + if (status > 0) + { + uint16 frag_length = *((uint16*)(pdu + 8)); + uint16 auth_length = *((uint16*)(pdu + 10)); + + STREAM* ntlmssp_stream = stream_new(0xFFFF); + stream_write(ntlmssp_stream, (pdu+(frag_length-auth_length)), auth_length); + ntlmssp_stream->p = ntlmssp_stream->data; + + ntlmssp_recv(rpch->ntlmssp, ntlmssp_stream); + + stream_free(ntlmssp_stream); + } + xfree(pdu); + return status; +} + +int rpch_write(rdpRpch* rpch, uint8* data, int length, uint16 opnum) +{ + int status = -1; + rpcconn_request_hdr_t* request_pdu; + int i; + rdpBlob rdpMsg; + STREAM* pdu; + uint8 auth_pad_length = (16 - ((24 + length + 8 + 16) % 16)) & 15; + + LLOGLN(10, ("rpch_write:")); + request_pdu = (rpcconn_request_hdr_t*) xmalloc(sizeof(rpcconn_request_hdr_t)); + request_pdu->rpc_vers = 5; + request_pdu->rpc_vers_minor = 0; + request_pdu->PTYPE = PTYPE_REQUEST; + request_pdu->pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG; + request_pdu->packed_drep[0] = 0x10; + request_pdu->packed_drep[1] = 0x00; + request_pdu->packed_drep[2] = 0x00; + request_pdu->packed_drep[3] = 0x00; + request_pdu->frag_length = 24 + length + auth_pad_length + 8 + 16; + request_pdu->auth_length = 16; + request_pdu->call_id = ++rpch->call_id; + LLOGLN(10, ("rpch_write: 1")); + if (opnum == 8) + { + LLOGLN(10, ("rpch_write: opnum is 8")); + /* opnum=8 means [MS-TSGU]TsProxySetupRecievePipe, save call_id for checking pipe responces */ + rpch->pipe_call_id = rpch->call_id; + } + request_pdu->alloc_hint = length; + request_pdu->p_cont_id = 0x0000; + request_pdu->opnum = opnum; + request_pdu->stub_data = data; + request_pdu->auth_verifier.auth_type = 0x0a; /* :01 which authent service */ + request_pdu->auth_verifier.auth_level = 0x05; /* :01 which level within service */ + request_pdu->auth_verifier.auth_pad_length = auth_pad_length; /* :01 */ + request_pdu->auth_verifier.auth_pad = xmalloc(auth_pad_length); /* align(4); size_is(auth_pad_length) p*/ + LLOGLN(10, ("rpch_write: 2")); + for (i = 0; i < auth_pad_length; i++) + { + request_pdu->auth_verifier.auth_pad[i] = 0x00; + } + request_pdu->auth_verifier.auth_reserved = 0x00; /* :01 reserved, m.b.z. */ + request_pdu->auth_verifier.auth_context_id = 0x00000000; /* :04 */ + request_pdu->auth_verifier.auth_value = xmalloc(request_pdu->auth_length); /* credentials; size_is(auth_length) p*/; + + pdu = stream_new(request_pdu->frag_length); + + LLOGLN(10, ("rpch_write: 3")); + stream_write(pdu, request_pdu, 24); + stream_write(pdu, request_pdu->stub_data, request_pdu->alloc_hint); + if (request_pdu->auth_verifier.auth_pad_length > 0) + { + stream_write(pdu, request_pdu->auth_verifier.auth_pad, request_pdu->auth_verifier.auth_pad_length); + } + LLOGLN(10, ("rpch_write: 3.1")); + stream_write(pdu, &request_pdu->auth_verifier.auth_type, 8); + + rdpMsg.data = pdu->data; + rdpMsg.length = (int) (pdu->p - pdu->data); + LLOGLN(10, ("rpch_write: 3.2")); + ntlmssp_encrypt_message(rpch->ntlmssp, &rdpMsg, NULL, request_pdu->auth_verifier.auth_value); + LLOGLN(10, ("rpch_write: 3.3")); + + LLOGLN(10, ("rpch_write: 4")); + stream_write(pdu, request_pdu->auth_verifier.auth_value, request_pdu->auth_length); + + status = rpch_in_write(rpch, pdu->data, pdu->p - pdu->data); + + xfree(request_pdu->auth_verifier.auth_value); + xfree(request_pdu->auth_verifier.auth_pad); + xfree(request_pdu); + + LLOGLN(10, ("rpch_write: 5")); + + if (status < 0) + { + LLOGLN(0, ("rpch_write: rcph_in_write failed")); + return status; + } + + LLOGLN(10, ("rpch_write: out")); + + return length; +} + +int rpch_read(rdpRpch* rpch, uint8* data, int length) +{/*TODO*/ + int rpch_length = 0xffff; // length + 0xFF; + int status; + int readed = 0; + int data_length; + uint8* rpch_data; + /*uint32 return_code;*/ + uint16 frag_length; + uint16 auth_length; + uint8 auth_pad_length; + uint32 call_id = -1; + + LLOGLN(10, ("rpch_read:")); + LLOGLN(10, ("rpch_read: read_buffer_len %d length %d", rpch->read_buffer_len, length)); + if (rpch->read_buffer_len > 0) + { + if (rpch->read_buffer_len > length) + { + /* TODO fix read_buffer is too long problem */ + printf("ERROR! RPCH Stores data in read_buffer fits not in data on rpch_read.\n"); + return -1; + } + memcpy(data, rpch->read_buffer, rpch->read_buffer_len); + readed += rpch->read_buffer_len; + xfree(rpch->read_buffer); + rpch->read_buffer_len = 0; + } + if (readed >= length) + { + return readed; + } + rpch_data = (uint8*) xmalloc(rpch_length); + while (true) + { + status = rpch_out_read(rpch, rpch_data, rpch_length); + if (status == 0) + { + xfree(rpch_data); + return readed; + } + else if (status < 0) + { + LLOGLN(10, ("rpch_read: error rpch_out_read failed")); + xfree(rpch_data); + return status; + } + + frag_length = *(uint16*)(rpch_data + 8); + auth_length = *(uint16*)(rpch_data + 10); + call_id = *(uint32*)(rpch_data + 12); + status = *(uint32*)(rpch_data + 16); /* alloc_hint */ + auth_pad_length = *(rpch_data + frag_length - auth_length - 6); /* -6 = -8 + 2 (sec_trailer + 2) */ + /* data_length must be calculated because alloc_hint carries size of more than one pdu */ + data_length = frag_length - auth_length - 24 - 8 - auth_pad_length; /* 24 is header; 8 is sec_trailer */ + if (status == 4) + { + LLOGLN(10, ("rpch_read: continue 1")); + continue; + } + if (readed + data_length > length) /* if readed data is greater then given buffer */ + { + LLOGLN(10, ("rpch_read: saving extra data")); + rpch->read_buffer_len = readed + data_length - length; + rpch->read_buffer = (uint8*) xmalloc(rpch->read_buffer_len); + data_length -= rpch->read_buffer_len; + memcpy(rpch->read_buffer, rpch_data + 24 + data_length, rpch->read_buffer_len); + } + memcpy(data + readed, rpch_data + 24, data_length); + readed += data_length; + if (status > data_length && readed < length) + { + LLOGLN(10, ("rpch_read: continue 2")); + continue; + } + break; + } + xfree(rpch_data); + return readed; +} + +tbool rpch_connect(rdpRpch* rpch) +{ + int pdu_length; + uint8* pdu; + int status; + rdpRpchHTTP* http_out = rpch->http_out; + rdpRpchHTTP* http_in = rpch->http_in; + + LLOGLN(10, ("rpch_connect:")); + + if (http_out->state == RPCH_HTTP_DISCONNECTED) + { + LLOGLN(10, ("rpch_connect: calling rpch_out_connect_http")); + if (!rpch_out_connect_http(rpch)) + { + LLOGLN(0, ("rpch_connect: rpch_out_connect_http failed")); + return false; + } + } + + if (!rpch_out_send_CONN_A1(rpch)) + { + LLOGLN(0, ("rpch_connect: rpch_out_send_CONN_A1 fault")); + return false; + } + + if (http_in->state == RPCH_HTTP_DISCONNECTED) + { + LLOGLN(10, ("rpch_connect: calling rpch_in_connect_http")); + if (!rpch_in_connect_http(rpch)) + { + LLOGLN(0, ("rpch_connect: rpch_in_connect_http failed")); + return -1; + } + } + + if (!rpch_in_send_CONN_B1(rpch)) + { + LLOGLN(0, ("rpch_connect: rpch_in_send_CONN_B1 fault")); + return false; + } + + LLOGLN(10, ("rpch_connect: 1")); + + /* Out Channel Response (section 2.1.2.1.4 ) */ + if (rpch_out_read_http_header(rpch) < 0) + { + LLOGLN(0, ("rpch_connect: rpch_out_read_http_header failed")); + return -1; + } + + if (http_out->contentLength != 0x40000000) + { + LLOGLN(0, ("rpch_connect: 'Bad Request'")); + return -1; + } + + pdu_length = 0xFFFF; + pdu = (uint8*) xmalloc(pdu_length); + + LLOGLN(10, ("rpch_connect: 2")); + + /* CONN/A3 RTS PDU (section 2.2.4.4 ) 28 bytes */ + status = rpch_out_read(rpch, pdu, pdu_length); + if (status < 0) + { + LLOGLN(0, ("rpch_connect: rpch_out_read failed")); + return false; + } + + /* CONN/C2 RTS PDU (section 2.2.4.9 ) 46 bytes */ + status = rpch_out_read(rpch, pdu, pdu_length); + if (status < 0) + { + LLOGLN(0, ("rpch_connect: rpch_out_read failed")); + return false; + } + + LLOGLN(10, ("rpch_connect: 3")); + + /* [MS-RPCH] 3.2.1.5.3.1 Connection Establishment + at this point VirtualChannel is created */ + + if (!rpch_in_send_bind(rpch)) + { + LLOGLN(0, ("rpch_connect: rpch_out_send_bind fault")); + return false; + } + LLOGLN(10, ("rpch_connect: 4")); + + if (!rpch_out_recv_bind_ack(rpch)) + { + LLOGLN(0, ("rpch_connect: rpch_out_recv_bind_ack fault")); + return false; + } + LLOGLN(10, ("rpch_connect: 5")); + + if (!rpch_in_send_rpc_auth_3(rpch)) + { + printf("rpch_out_send_rpc_auth_3 fault!\n"); + return false; + } + LLOGLN(10, ("rpch_connect: 6")); + + xfree(pdu); + LLOGLN(10, ("rpch_connect: out")); + return true; +} + +/* check if pdu can be skipped by upper layers eg it's a control pdu */ +tbool rpch_skip_pdu(rdpRpch* rpch, STREAM* s) +{ + uint8* phold; + int frag_length; + int auth_length; + int call_id; + int status; + int ptype; + int pfc_flags; + + phold = s->p; + s->p += 2; + stream_read_uint8(s, ptype); + stream_read_uint8(s, pfc_flags); + s->p += 4; + stream_read_uint16(s, frag_length); + stream_read_uint16(s, auth_length); + stream_read_uint32(s, call_id); + stream_read_uint32(s, status); + LLOGLN(10, ("rpch_skip_pdu: ptype %d pfc_flags %d frag_length %d " + "auth_length %d call_id %d status %d", ptype, pfc_flags, + frag_length, auth_length, call_id, status)); + + if (ptype == 0x14) + { + LLOGLN(10, ("rpch_skip_pdu: calling rpch_proceed_RTS")); + rpch_proceed_RTS(rpch, s->data, frag_length); + return true; + } + + rpch->BytesReceived += frag_length; + //rpch->total_for_flow += frag_length; + rpch->AwailableWindow -= frag_length; + + if (call_id == 5 && status == 4) + { + LLOGLN(0, ("rpch_skip_pdu: yup, 4")); + } + + //if (status == 0x04) + if (call_id != 5) + { + return true; + } + + //if (rpch->total_for_flow > 16 * 1024) + //if (rpch->total_for_flow > 35535) + if (rpch->AwailableWindow < 0x00008FFF) /* Just a simple workaround */ + { + LLOGLN(10, ("rpch_skip_pdu: calling rpch_in_send_flow_control")); + //rpch->BytesReceived = 0; + rpch_in_send_flow_control(rpch); + //rpch->total_for_flow = 0; + } + + s->p = phold + 24; + return false; +} diff --git a/libfreerdp-core/rpch.h b/libfreerdp-core/rpch.h new file mode 100644 index 0000000..5a12ad3 --- /dev/null +++ b/libfreerdp-core/rpch.h @@ -0,0 +1,630 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * RDP Security + * + * Copyright 2012 Fujitsu Technology Solutions GmbH - Dmitrij Jasnov <dmitrij.jasnov@ts.fujitsu.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __RPCH_H_ +#define __RPCH_H_ + +typedef struct rdp_rpch rdpRpch; +typedef struct rdp_rpch_http rdpRpchHTTP; + +#include "tcp.h" +#include "ntlmssp.h" +#include "tls.h" +#include "crypto.h" + +#include <time.h> +#include <freerdp/types.h> +#include <freerdp/settings.h> +#include <freerdp/utils/wait_obj.h> +#include <freerdp/utils/sleep.h> +#include <freerdp/utils/stream.h> +#include <freerdp/utils/memory.h> +#include <freerdp/utils/hexdump.h> + +#define PTYPE_REQUEST 0x00 +#define PTYPE_PING 0x01 +#define PTYPE_RESPONSE 0x02 +#define PTYPE_FAULT 0x03 +#define PTYPE_WORKING 0x04 +#define PTYPE_NOCALL 0x05 +#define PTYPE_REJECT 0x06 +#define PTYPE_ACK 0x07 +#define PTYPE_CL_CANCEL 0x08 +#define PTYPE_FACK 0x09 +#define PTYPE_CANCEL_ACK 0x0a +#define PTYPE_BIND 0x0b +#define PTYPE_BIND_ACK 0x0c +#define PTYPE_BIND_NAK 0x0d +#define PTYPE_ALTER_CONTEXT 0x0e +#define PTYPE_ALTER_CONTEXT_RESP 0x0f +#define PTYPE_RPC_AUTH_3 0x10 +#define PTYPE_SHUTDOWN 0x11 +#define PTYPE_CO_CANCEL 0x12 +#define PTYPE_ORPHANED 0x13 +#define PTYPE_RTS 0x14 + +#define PFC_FIRST_FRAG 0x01 /* First fragment */ +#define PFC_LAST_FRAG 0x02 /* Last fragment */ +#define PFC_PENDING_CANCEL 0x04 /* Cancel was pending at sender */ +#define PFC_RESERVED_1 0x08 +#define PFC_CONC_MPX 0x10 /* supports concurrent multiplexing + * of a single connection. */ +#define PFC_DID_NOT_EXECUTE 0x20 /* only meaningful on `fault' packet; + * if true, guaranteed call did not + * execute. */ +#define PFC_MAYBE 0x40 /* 'maybe' call semantics requested */ +#define PFC_OBJECT_UUID 0x80 /* if true, a non-nil object UUID + * was specified in the handle, and + * is present in the optional object + * field. If false, the object field + * is omitted. */ + +#define RTS_FLAG_NONE 0x0000 /* No special flags. */ +#define RTS_FLAG_PING 0x0001 /* Proves that the sender is still active; can also be used to flush the pipeline by the other party. */ +#define RTS_FLAG_OTHER_CMD 0x0002 /* Indicates that the PDU contains a command that cannot be defined by the other flags in this table. */ +#define RTS_FLAG_RECYCLE_CHANNEL 0x0004 /* Indicates that the PDU is associated with recycling a channel. */ +#define RTS_FLAG_IN_CHANNEL 0x0008 /* Indicates that the PDU is associated with IN channel communications. */ +#define RTS_FLAG_OUT_CHANNEL 0x0010 /* Indicates that the PDU is associated with OUT channel communications. */ +#define RTS_FLAG_EOF 0x0020 /* Indicates that this is the last PDU on an IN channel or OUT channel. Not all channels, however, use this to indicate the last PDU. */ +#define RTS_FLAG_ECHO 0x0040 /* Signifies that this PDU is an echo request or response. */ + +typedef uint16 p_context_id_t; + +typedef struct +{ + uuid if_uuid; + uint32 if_version; +} p_syntax_id_t; + +typedef struct +{ + p_context_id_t p_cont_id; + uint8 n_transfer_syn; /* number of items */ + uint8 reserved; /* alignment pad, m.b.z. */ + p_syntax_id_t abstract_syntax; /* transfer syntax list */ + p_syntax_id_t* transfer_syntaxes; /* size_is(n_transfer_syn)*/ +} p_cont_elem_t; + +typedef struct +{ + uint8 n_context_elem; /* number of items */ + uint8 reserved; /* alignment pad, m.b.z. */ + uint16 reserved2; /* alignment pad, m.b.z. */ + p_cont_elem_t* p_cont_elem; /*size_is(n_cont_elem) */ +} p_cont_list_t; + +typedef enum _p_cont_def_result_t +{ + acceptance, + user_rejection, + provider_rejection +} p_cont_def_result_t; + +typedef enum +{ + reason_not_specified, + abstract_syntax_not_supported, + proposed_transfer_syntaxes_not_supported, + local_limit_exceeded +} p_provider_reason_t; + + +typedef struct +{ + p_cont_def_result_t result; + p_provider_reason_t reason; /* only relevant if result != acceptance */ + p_syntax_id_t transfer_syntax; /* tr syntax selected 0 if result not accepted */ +} p_result_t; + +/* Same order and number of elements as in bind request */ + +typedef struct +{ + uint8 n_results; /* count */ + uint8 reserved; /* alignment pad, m.b.z. */ + uint16 reserved2; /* alignment pad, m.b.z. */ + p_result_t* p_results; /* size_is(n_results) */ +} p_result_list_t; + +typedef struct +{ + uint8 major; + uint8 minor; +} version_t; + +typedef version_t p_rt_version_t; + +typedef struct +{ + uint8 n_protocols; /* count */ + p_rt_version_t* p_protocols; /* size_is(n_protocols) */ +} p_rt_versions_supported_t; + +typedef struct +{ + uint16 length; + char* port_spec; /* port string spec; size_is(length) */ +} port_any_t; + +#define REASON_NOT_SPECIFIED 0 +#define TEMPORARY_CONGESTION 1 +#define LOCAL_LIMIT_EXCEEDED 2 +#define CALLED_PADDR_UNKNOWN 3 /* not used */ +#define PROTOCOL_VERSION_NOT_SUPPORTED 4 +#define DEFAULT_CONTEXT_NOT_SUPPORTED 5 /* not used */ +#define USER_DATA_NOT_READABLE 6 /* not used */ +#define NO_PSAP_AVAILABLE 7 /* not used */ + +typedef uint16 rpcrt_reason_code_t;/* 0..65535 */ + +typedef struct { + uint8 rpc_vers; + uint8 rpc_vers_minor; + uint8 reserved[2];/* must be zero */ + uint8 packed_drep[4]; + uint32 reject_status; + uint8 reserved2[4]; +} rpcrt_optional_data_t; + +typedef struct { + rpcrt_reason_code_t reason_code; /* 0..65535 */ + rpcrt_optional_data_t rpc_info; /* may be RPC specific */ +} rpcconn_reject_optional_data_t; + +typedef struct { + rpcrt_reason_code_t reason_code; /* 0..65535 */ + rpcrt_optional_data_t rpc_info; /* may be RPC-specific */ +} rpcconn_disc_optional_data_t; + +typedef struct{ + /* restore 4 byte alignment */ + + uint8* auth_pad; /* align(4); size_is(auth_pad_length) */ + uint8 auth_type; /* :01 which authent service */ + uint8 auth_level; /* :01 which level within service */ + uint8 auth_pad_length; /* :01 */ + uint8 auth_reserved; /* :01 reserved, m.b.z. */ + uint32 auth_context_id; /* :04 */ + uint8* auth_value; /* credentials; size_is(auth_length) */ +} auth_verifier_co_t; + +/* Connection-oriented PDU Definitions */ + +typedef struct _rpcconn_alter_context_hdr_t { + /* start 8-octet aligned */ + + /* common fields */ + uint8 rpc_vers; /* 00:01 RPC version */ + uint8 rpc_vers_minor ; /* 01:01 minor version */ + uint8 PTYPE; /* 02:01 alter context PDU */ + uint8 pfc_flags; /* 03:01 flags */ + uint8 packed_drep[4]; /* 04:04 NDR data rep format label */ + uint16 frag_length; /* 08:02 total length of fragment */ + uint16 auth_length; /* 10:02 length of auth_value */ + uint32 call_id; /* 12:04 call identifier */ + + /* end common fields */ + + uint16 max_xmit_frag; /* ignored */ + uint16 max_recv_frag; /* ignored */ + uint32 assoc_group_id; /* ignored */ + + /* presentation context list */ + + p_cont_list_t p_context_elem; /* variable size */ + + /* optional authentication verifier */ + /* following fields present iff auth_length != 0 */ + + auth_verifier_co_t auth_verifier; + +} rpcconn_alter_context_hdr_t; + +typedef struct _rpcconn_alter_context_response_hdr_t { + /* start 8-octet aligned */ + + /* common fields */ + uint8 rpc_vers; /* 00:01 RPC version */ + uint8 rpc_vers_minor; /* 01:01 minor version */ + uint8 PTYPE; /* 02:01 alter + context response PDU */ + uint8 pfc_flags; /* 03:01 flags */ + uint8 packed_drep[4]; /* 04:04 NDR data rep format label*/ + uint16 frag_length; /* 08:02 total length of fragment */ + uint16 auth_length; /* 10:02 length of auth_value */ + uint32 call_id; /* 12:04 call identifier */ + + /* end common fields */ + + uint16 max_xmit_frag; /* ignored */ + uint16 max_recv_frag; /* ignored */ + uint32 assoc_group_id; /* ignored */ + port_any_t sec_addr; /* ignored */ + + /* restore 4-octet alignment */ + + uint8* pad2; /* size_is(align(4)) */ + + /* presentation context result list, including hints */ + + p_result_list_t p_result_list; /* variable size */ + + /* optional authentication verifier */ + /* following fields present iff auth_length != 0 */ + + auth_verifier_co_t auth_verifier; /* xx:yy */ +} rpcconn_alter_context_response_hdr_t; + +/* bind header */ +typedef struct _rpcconn_bind_hdr_t +{ + /* start 8-octet aligned */ + + /* common fields */ + uint8 rpc_vers; /* 00:01 RPC version */ + uint8 rpc_vers_minor; /* 01:01 minor version */ + uint8 PTYPE; /* 02:01 bind PDU */ + uint8 pfc_flags; /* 03:01 flags */ + uint8 packed_drep[4]; /* 04:04 NDR data rep format label*/ + uint16 frag_length; /* 08:02 total length of fragment */ + uint16 auth_length; /* 10:02 length of auth_value */ + uint32 call_id; /* 12:04 call identifier */ + + /* end common fields */ + + uint16 max_xmit_frag; /* 16:02 max transmit frag size, bytes */ + uint16 max_recv_frag; /* 18:02 max receive frag size, bytes */ + uint32 assoc_group_id; /* 20:04 incarnation of client-server + * assoc group */ + /* presentation context list */ + + p_cont_list_t p_context_elem; /* variable size */ + + /* optional authentication verifier */ + /* following fields present iff auth_length != 0 */ + + auth_verifier_co_t auth_verifier; +} rpcconn_bind_hdr_t; + +typedef struct _rpcconn_bind_ack_hdr_t +{ + /* start 8-octet aligned */ + + /* common fields */ + uint8 rpc_vers; /* 00:01 RPC version */ + uint8 rpc_vers_minor ; /* 01:01 minor version */ + uint8 PTYPE; /* 02:01 bind ack PDU */ + uint8 pfc_flags; /* 03:01 flags */ + uint8 packed_drep[4]; /* 04:04 NDR data rep format label*/ + uint16 frag_length; /* 08:02 total length of fragment */ + uint16 auth_length; /* 10:02 length of auth_value */ + uint32 call_id; /* 12:04 call identifier */ + + /* end common fields */ + + uint16 max_xmit_frag; /* 16:02 max transmit frag size */ + uint16 max_recv_frag; /* 18:02 max receive frag size */ + uint32 assoc_group_id; /* 20:04 returned assoc_group_id */ + port_any_t sec_addr; /* 24:yy optional secondary address + * for process incarnation; local port + * part of address only */ + /* restore 4-octet alignment */ + + uint8* pad2; /* size_is(align(4)) */ + + /* presentation context result list, including hints */ + + p_result_list_t p_result_list; /* variable size */ + + /* optional authentication verifier */ + /* following fields present iff auth_length != 0 */ + + auth_verifier_co_t auth_verifier; /* xx:yy */ +} rpcconn_bind_ack_hdr_t; + +typedef struct _rpcconn_rpc_auth_3_hdr_t +{ + /* start 8-octet aligned */ + + /* common fields */ + uint8 rpc_vers; /* 00:01 RPC version */ + uint8 rpc_vers_minor ; /* 01:01 minor version */ + uint8 PTYPE; /* 02:01 bind ack PDU */ + uint8 pfc_flags; /* 03:01 flags */ + uint8 packed_drep[4]; /* 04:04 NDR data rep format label*/ + uint16 frag_length; /* 08:02 total length of fragment */ + uint16 auth_length; /* 10:02 length of auth_value */ + uint32 call_id; /* 12:04 call identifier */ + + /* end common fields */ + + uint16 max_xmit_frag; /* 16:02 max transmit frag size */ + uint16 max_recv_frag; /* 18:02 max receive frag size */ + + /* optional authentication verifier */ + /* following fields present iff auth_length != 0 */ + + auth_verifier_co_t auth_verifier; /* xx:yy */ +} rpcconn_rpc_auth_3_hdr_t; + +typedef struct _rpcconn_bind_nak_hdr_t +{ + /* start 8-octet aligned */ + + /* common fields */ + uint8 rpc_vers; /* 00:01 RPC version */ + uint8 rpc_vers_minor ; /* 01:01 minor version */ + uint8 PTYPE; /* 02:01 bind nak PDU */ + uint8 pfc_flags; /* 03:01 flags */ + uint8 packed_drep[4]; /* 04:04 NDR data rep format label*/ + uint16 frag_length; /* 08:02 total length of fragment */ + uint16 auth_length; /* 10:02 length of auth_value */ + uint32 call_id; /* 12:04 call identifier */ + + /* end common fields */ + + /*p_reject_reason_t*/uint16 provider_reject_reason; /* 16:02 presentation (TODO search definition of p_reject_reason_t) + context reject */ + + p_rt_versions_supported_t versions; /* 18:yy array of protocol + * versions supported */ +} rpcconn_bind_nak_hdr_t; + + +typedef struct _rpcconn_cancel_hdr_t +{ + /* start 8-octet aligned */ + + /* common fields */ + uint8 rpc_vers; /* 00:01 RPC version */ + uint8 rpc_vers_minor; /* 01:01 minor version */ + uint8 PTYPE; /* 02:01 CO cancel PDU */ + uint8 pfc_flags; /* 03:01 flags */ + uint8 packed_drep[4]; /* 04:04 NDR data rep format label*/ + uint16 frag_length; /* 08:02 total length of fragment */ + uint16 auth_length; /* 10:02 length of auth_value */ + uint32 call_id; /* 12:04 call identifier */ + + /* end common fields */ + + /* optional authentication verifier + * following fields present iff auth_length != 0 */ + + auth_verifier_co_t auth_verifier; /* xx:yy */ + +} rpcconn_cancel_hdr_t; + +typedef struct _rpcconn_fault_hdr_t +{ + /* start 8-octet aligned */ + + /* common fields */ + uint8 rpc_vers; /* 00:01 RPC version */ + uint8 rpc_vers_minor; /* 01:01 minor version */ + uint8 PTYPE; /* 02:01 fault PDU */ + uint8 pfc_flags; /* 03:01 flags */ + uint8 packed_drep[4]; /* 04:04 NDR data rep format label */ + uint16 frag_length; /* 08:02 total length of fragment */ + uint16 auth_length; /* 10:02 length of auth_value */ + uint32 call_id; /* 12:04 call identifier */ + + /* end common fields */ + + /* needed for request, response, fault */ + + uint32 alloc_hint; /* 16:04 allocation hint */ + p_context_id_t p_cont_id; /* 20:02 pres context, i.e. data rep */ + + /* needed for response or fault */ + + uint8 cancel_count; /* 22:01 received cancel count */ + uint8 reserved; /* 23:01 reserved, m.b.z. */ + + /* fault code */ + + uint32 status; /* 24:04 run-time fault code or zero */ + + /* always pad to next 8-octet boundary */ + + uint8 reserved2[4]; /* 28:04 reserved padding, m.b.z. */ + + /* stub data here, 8-octet aligned + . + . + . */ + uint8* stub_data; + + /* optional authentication verifier */ + /* following fields present iff auth_length != 0 */ + + auth_verifier_co_t auth_verifier; /* xx:yy */ +} rpcconn_fault_hdr_t; + + +typedef struct _rpcconn_orphaned_hdr_t +{ + /* start 8-octet aligned */ + + /* common fields */ + uint8 rpc_vers; /* 00:01 RPC version */ + uint8 rpc_vers_minor; /* 01:01 minor version */ + uint8 PTYPE; /* 02:01 orphaned PDU */ + uint8 pfc_flags; /* 03:01 flags */ + uint8 packed_drep[4]; /* 04:04 NDR data rep format label*/ + uint16 frag_length; /* 08:02 total length of fragment */ + uint16 auth_length; /* 10:02 length of auth_value */ + uint32 call_id; /* 12:04 call identifier */ + + /* end common fields */ + + /* optional authentication verifier + * following fields present iff auth_length != 0 */ + + auth_verifier_co_t auth_verifier; /* xx:yy */ +} rpcconn_orphaned_hdr_t; + + +typedef struct _rpcconn_request_hdr_t +{ + /* start 8-octet aligned */ + + /* common fields */ + uint8 rpc_vers; /* 00:01 RPC version */ + uint8 rpc_vers_minor; /* 01:01 minor version */ + uint8 PTYPE; /* 02:01 request PDU */ + uint8 pfc_flags; /* 03:01 flags */ + uint8 packed_drep[4]; /* 04:04 NDR data rep format label */ + uint16 frag_length; /* 08:02 total length of fragment */ + uint16 auth_length; /* 10:02 length of auth_value */ + uint32 call_id; /* 12:04 call identifier */ + + /* end common fields */ + + /* needed on request, response, fault */ + + uint32 alloc_hint; /* 16:04 allocation hint */ + p_context_id_t p_cont_id; /* 20:02 pres context, i.e. data rep */ + uint16 opnum; /* 22:02 operation # within the interface */ + + /* optional field for request, only present if the PFC_OBJECT_UUID + * field is non-zero */ + + uuid object; /* 24:16 object UID */ + + /* stub data, 8-octet aligned + . + . + . */ + uint8* stub_data; + + /* optional authentication verifier */ + /* following fields present iff auth_length != 0 */ + + auth_verifier_co_t auth_verifier; /* xx:yy */ +} rpcconn_request_hdr_t; + +typedef struct _rpcconn_response_hdr_t +{ + /* start 8-octet aligned */ + + /* common fields */ + uint8 rpc_vers; /* 00:01 RPC version */ + uint8 rpc_vers_minor; /* 01:01 minor version */ + uint8 PTYPE; /* 02:01 response PDU */ + uint8 pfc_flags; /* 03:01 flags */ + uint8 packed_drep[4]; /* 04:04 NDR data rep format label*/ + uint16 frag_length; /* 08:02 total length of fragment */ + uint16 auth_length; /* 10:02 length of auth_value */ + uint32 call_id; /* 12:04 call identifier */ + + /* end common fields */ + + /* needed for request, response, fault */ + + uint32 alloc_hint; /* 16:04 allocation hint */ + p_context_id_t p_cont_id; /* 20:02 pres context, i.e. data rep */ + + /* needed for response or fault */ + + uint8 cancel_count; /* 22:01 cancel count */ + uint8 reserved; /* 23:01 reserved, m.b.z. */ + + /* stub data here, 8-octet aligned */ + uint8* stub_data; + + /* optional authentication verifier */ + /* following fields present iff auth_length != 0 */ + + auth_verifier_co_t auth_verifier; /* xx:yy */ +} rpcconn_response_hdr_t; + +typedef struct _rpcconn_shutdown_hdr_t +{ + /* start 8-octet aligned */ + + /* common fields */ + uint8 rpc_vers; /* 00:01 RPC version */ + uint8 rpc_vers_minor; /* 01:01 minor version */ + uint8 PTYPE; /* 02:01 shutdown PDU */ + uint8 pfc_flags; /* 03:01 flags */ + uint8 packed_drep[4]; /* 04:04 NDR data rep format label*/ + uint16 frag_length; /* 08:02 total length of fragment */ + uint16 auth_length; /* 10:02 */ + uint32 call_id; /* 12:04 call identifier */ + + /* end common fields */ +} rpcconn_shutdown_hdr_t; + +/* proprietary stuff from Dmitrij Jasnov */ + +#define RPCH_HTTP_DISCONNECTED 0 +#define RPCH_HTTP_SENDING 1 +#define RPCH_HTTP_RECIEVING 2 + +struct rdp_rpch_http +{ + int state; + int contentLength; + int remContentLength; + struct _NTLMSSP *ntht; +}; + +struct rdp_rpch +{ + struct rdp_settings* settings; + struct rdp_tcp* tcp_in; + struct rdp_tcp* tcp_out; + struct rdp_tls* tls_in; + struct rdp_tls* tls_out; + struct _NTLMSSP *ntlmssp; + + struct rdp_rpch_http* http_in; + struct rdp_rpch_http* http_out; + + uint8* write_buffer; + uint32 write_buffer_len; + uint8* read_buffer; + uint32 read_buffer_len; + + uint32 BytesReceived; + uint32 AwailableWindow; + uint32 BytesSent; + uint32 RecAwailableWindow; + uint8* virtualConnectionCookie; + uint8* OUTChannelCookie; + uint8* INChannelCookie; + uint32 call_id; + uint32 pipe_call_id; + int total_for_flow; +}; + +rdpRpch* rpch_new(rdpSettings* settings); +tbool rpch_attach(rdpRpch* rpch, rdpTcp* tcp_in, rdpTcp* tcp_out, struct rdp_tls* tls_in, struct rdp_tls* tls_out); +tbool rpch_connect(rdpRpch* rpch); +int rpch_write(rdpRpch* rpch, uint8* data, int length, uint16 opnum); +int rpch_read(rdpRpch* rpch, uint8* data, int length); +tbool rpch_skip_pdu(rdpRpch* rpch, STREAM* s); + +#ifdef WITH_DEBUG_RPCH +#define DEBUG_RPCH(fmt, ...) DEBUG_CLASS(RPCH, fmt, ## __VA_ARGS__) +#else +#define DEBUG_RPCH(fmt, ...) DEBUG_NULL(fmt, ## __VA_ARGS__) +#endif + +#endif /* __RPCH_H_ */ diff --git a/libfreerdp-core/tsg.c b/libfreerdp-core/tsg.c new file mode 100644 index 0000000..5053252 --- /dev/null +++ b/libfreerdp-core/tsg.c @@ -0,0 +1,309 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * Gateway + * + * Copyright 2012 Fujitsu Technology Solutions GmbH - + * Dmitrij Jasnov <dmitrij.jasnov@ts.fujitsu.com> + * Copyright 2014 Jay Sorg - jay.sorg@gmail.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <freerdp/utils/sleep.h> +#include <freerdp/utils/stream.h> +#include <freerdp/utils/memory.h> +#include <freerdp/utils/hexdump.h> +#include <freerdp/utils/unicode.h> + +#include "tsg.h" + +static uint8 g_p1[108] = { + 0x43, 0x56, 0x00, 0x00, 0x43, 0x56, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x52, 0x54, 0x43, 0x56, + 0x04, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, + 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 }; +static uint8 g_p2[112] = { + 0x00, 0x00, 0x00, 0x00, 0x6A, 0x78, 0xE9, 0xAB, + 0x02, 0x90, 0x1C, 0x44, 0x8D, 0x99, 0x29, 0x30, + 0x53, 0x6C, 0x04, 0x33, 0x52, 0x51, 0x00, 0x00, + 0x52, 0x51, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0x2D, 0x00, + 0x4E, 0x00, 0x48, 0x00, 0x35, 0x00, 0x37, 0x00, + 0x30, 0x00, 0x2E, 0x00, 0x43, 0x00, 0x53, 0x00, + 0x4F, 0x00, 0x44, 0x00, 0x2E, 0x00, 0x6C, 0x00, + 0x6F, 0x00, 0x63, 0x00, 0x61, 0x00, 0x6C, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static uint8 g_p3[40] = { + 0x00, 0x00, 0x00, 0x00, 0x6A, 0x78, 0xE9, 0xAB, + 0x02, 0x90, 0x1C, 0x44, 0x8D, 0x99, 0x29, 0x30, + 0x53, 0x6C, 0x04, 0x33, 0x01, 0x00, 0x00, 0x00, + 0x52, 0x47, 0x00, 0x00, 0x52, 0x47, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00 }; +static uint8 g_p4[48] = { + 0x00, 0x00, 0x00, 0x00, 0x6A, 0x78, 0xE9, 0xAB, + 0x02, 0x90, 0x1C, 0x44, 0x8D, 0x99, 0x29, 0x30, + 0x53, 0x6C, 0x04, 0x33, 0x00, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00 }; +static uint8 g_p5[20] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; + +//#define WITH_DEBUG_TSG 1 +#define LLOG_LEVEL 1 +#define LLOGLN(_level, _args) \ + do { if (_level < LLOG_LEVEL) { printf _args ; printf("\n"); } } while (0) + +static tbool tsg_get_tunnel_context(uint8* data, int data_bytes, uint8* tunnel_context) +{ + //uint8* ldata = data; + //uint8* lend = data + data_bytes; + + LLOGLN(10, ("get_tunnel_context: data_bytes %d", data_bytes)); + //while(ldata < lend) + //{ + // ldata++; + //} + memcpy(tunnel_context, data + data_bytes - 60 + 36, 16); + return true; +} + +tbool tsg_connect(rdpTsg* tsg, const char* hostname, uint16 port) +{ + int status = -1; + rdpRpch* rpch = tsg->rpch; + rdpTransport* transport = tsg->transport; + uint32 length; + uint32 dest_addr_unic_len; + uint8* data; + uint8* dest_addr_unic; + UNICONV* tsg_uniconv; + STREAM* s_p4; + + LLOGLN(10, ("tsg_connect:")); + if (!rpch_attach(rpch, transport->tcp_in, transport->tcp_out, + transport->tls_in, transport->tls_out)) + { + LLOGLN(0, ("tsg_connect: rpch_attach failed")); + return false; + } + LLOGLN(10, ("tsg_connect: rpch_attach ok")); + if (!rpch_connect(rpch)) + { + LLOGLN(0, ("tsg_connect: rpch_connect failed")); + return false; + } + LLOGLN(10, ("tsg_connect: rpch_connect ok")); + + status = rpch_write(rpch, g_p1, 108, 1); + if (status <= 0) + { + LLOGLN(0, ("tsg_connect: rpch_write opnum=1 failed")); + return false; + } + + LLOGLN(10, ("tsg_connect: 1")); + + /* read certificate here */ + length = 0x8FFF; + data = xmalloc(length); + status = rpch_read(rpch, data, length); + if (status <= 0) + { + printf("rpch_recv failed!\n"); + return false; + } + + if (!tsg_get_tunnel_context(data, status, tsg->tunnelContext)) + { + printf("get_tunnel_context failed!\n"); + return false; + } + +#ifdef WITH_DEBUG_TSG + printf("TSG tunnelContext:\n"); + freerdp_hexdump(tsg->tunnelContext, 16); + printf("\n"); +#endif + + memcpy(g_p2 + 4, tsg->tunnelContext, 16); + + LLOGLN(10, ("tsg_connect: 2")); + + status = rpch_write(rpch, g_p2, 112, 2); + if (status <= 0) + { + printf("rpch_write opnum=2 failed!\n"); + return false; + } + + LLOGLN(10, ("tsg_connect: 3")); + + status = rpch_read(rpch, data, length); + if (status <= 0) + { + printf("rpch_recv failed!\n"); + return false; + } + + LLOGLN(10, ("tsg_connect: 4")); + + memcpy(g_p3 + 4, tsg->tunnelContext, 16); + status = rpch_write(rpch, g_p3, 40, 3); + if (status <= 0) + { + printf("rpch_write opnum=3 failed!\n"); + return false; + } + status = -1; + + tsg_uniconv = freerdp_uniconv_new(); + dest_addr_unic = (uint8*)freerdp_uniconv_out(tsg_uniconv, hostname, (size_t*)&dest_addr_unic_len); + freerdp_uniconv_free(tsg_uniconv); + + memcpy(g_p4 + 4, tsg->tunnelContext, 16); + memcpy(g_p4 + 38, &port, 2); + + s_p4 = stream_new(60 + dest_addr_unic_len + 2); + stream_write(s_p4, g_p4, 48); + stream_write_uint32(s_p4, (dest_addr_unic_len / 2) + 1); /* MaximumCount */ + stream_write_uint32(s_p4, 0x00000000); /* Offset */ + stream_write_uint32(s_p4, (dest_addr_unic_len / 2) + 1); /* ActualCount */ + stream_write(s_p4, dest_addr_unic, dest_addr_unic_len); + stream_write_uint16(s_p4, 0x0000); /* unicode zero to terminate hostname string */ + + LLOGLN(10, ("tsg_connect: 5")); + + status = rpch_write(rpch, s_p4->data, s_p4->size, 4); + if (status <= 0) + { + printf("rpch_write opnum=4 failed!\n"); + return false; + } + xfree(dest_addr_unic); + + LLOGLN(10, ("tsg_connect: 6")); + + status = rpch_read(rpch, data, length); + if (status < 0) + { + printf("rpch_recv failed!\n"); + return false; + } + + memcpy(tsg->channelContext, data + 4, 16); + +#ifdef WITH_DEBUG_TSG + printf("TSG channelContext:\n"); + freerdp_hexdump(tsg->channelContext, 16); + printf("\n"); +#endif + + LLOGLN(10, ("tsg_connect: 7")); + + memcpy(g_p5 + 4, tsg->channelContext, 16); + status = rpch_write(rpch, g_p5, 20, 8); + if (status <= 0) + { + printf("rpch_write opnum=8 failed!\n"); + return false; + } + + LLOGLN(10, ("tsg_connect: out")); + + return true; +} + +int tsg_write(rdpTsg* tsg, uint8* data, uint32 length) +{ + int status; + uint16 opnum = 9; + uint32 tsg_length; + uint32 totalDataBytes; + uint8 pp[8] = { 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 }; + STREAM* s; + uint8* tsg_pkg; + + LLOGLN(10, ("tsg_write: int length %d", length)); + tsg_length = length + 16 + 4 + 12 + 8; + totalDataBytes = length + 4; + s = stream_new(12); + stream_write_uint32_be(s, totalDataBytes); + stream_write_uint32_be(s, 0x01); + stream_write_uint32_be(s, length); + tsg_pkg = (uint8*) xmalloc(tsg_length); + memset(tsg_pkg, 0, 4); + memcpy(tsg_pkg + 4, tsg->channelContext, 16); + memcpy(tsg_pkg + 20, s->data, 12); + memcpy(tsg_pkg + 32, data, length); + memcpy(tsg_pkg + 32 + length, pp, 8); + status = rpch_write(tsg->rpch, tsg_pkg, tsg_length, opnum); + xfree(tsg_pkg); + stream_free(s); + if (status <= 0) + { + printf("rpch_write failed!\n"); + return -1; + } + LLOGLN(10, ("tsg_write: out length %d", length)); + return length; +} + +int tsg_read(rdpTsg* tsg, uint8* data, uint32 length) +{ + int status; + + LLOGLN(10, ("tsg_read:")); + status = rpch_read(tsg->rpch, data, length); + return status; +} + +tbool tsg_skip_pdu(rdpTsg* tsg, STREAM* s) +{ + return rpch_skip_pdu(tsg->rpch, s); +} + +rdpTsg* tsg_new(rdpSettings* settings) +{ + rdpTsg* tsg; + + tsg = (rdpTsg*) xzalloc(sizeof(rdpTsg)); + tsg->settings = settings; + tsg->rpch = rpch_new(settings); + return tsg; +} + +void tsg_free(rdpTsg* tsg) +{ + xfree(tsg); +} diff --git a/libfreerdp-core/tsg.h b/libfreerdp-core/tsg.h new file mode 100644 index 0000000..795df66 --- /dev/null +++ b/libfreerdp-core/tsg.h @@ -0,0 +1,59 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * RDP Security + * + * Copyright 2012 Fujitsu Technology Solutions GmbH - + * Dmitrij Jasnov <dmitrij.jasnov@ts.fujitsu.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TSG_H_ +#define __TSG_H_ + +typedef struct rdp_tsg rdpTsg; + +#include "transport.h" +#include "rpch.h" + +#include <time.h> +#include <freerdp/types.h> +#include <freerdp/settings.h> +#include <freerdp/utils/stream.h> +#include <freerdp/utils/wait_obj.h> +#include <freerdp/utils/debug.h> + +struct rdp_tsg +{ + struct rdp_settings* settings; + struct rdp_transport* transport; + struct rdp_rpch* rpch; + + uint8 tunnelContext[16]; + uint8 channelContext[16]; +}; + +tbool tsg_connect(rdpTsg* tsg, const char* hostname, uint16 port); +int tsg_write(rdpTsg* tsg, uint8* data, uint32 length); +int tsg_read(rdpTsg* tsg, uint8* data, uint32 length); +tbool tsg_skip_pdu(rdpTsg* tsg, STREAM* s); +rdpTsg* tsg_new(rdpSettings* settings); +void tsg_free(rdpTsg* tsg); + +#ifdef WITH_DEBUG_TSG +#define DEBUG_TSG(fmt, ...) DEBUG_CLASS(TSG, fmt, ## __VA_ARGS__) +#else +#define DEBUG_TSG(fmt, ...) DEBUG_NULL(fmt, ## __VA_ARGS__) +#endif + +#endif /* __TSG_H_ */ |