FreeRDP/libfreerdp/core/gateway/rpc.c

941 lines
25 KiB
C
Raw Normal View History

/**
2012-10-09 07:02:04 +04:00
* FreeRDP: A Remote Desktop Protocol Implementation
* RPC over HTTP
*
* Copyright 2012 Fujitsu Technology Solutions GmbH
* Copyright 2012 Dmitrij Jasnov <dmitrij.jasnov@ts.fujitsu.com>
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
2012-09-09 00:45:06 +04:00
#include <winpr/crt.h>
2012-10-29 06:16:21 +04:00
#include <winpr/tchar.h>
#include <winpr/synch.h>
#include <winpr/dsparse.h>
2014-08-19 20:26:39 +04:00
#include <freerdp/log.h>
#include <openssl/rand.h>
#include <openssl/bio.h>
#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#endif
2012-04-14 22:19:31 +04:00
#include "http.h"
#include "ntlm.h"
#include "ncacn_http.h"
#include "rpc_bind.h"
#include "rpc_fault.h"
#include "rpc_client.h"
2012-04-14 22:19:31 +04:00
#include "rpc.h"
#define TAG FREERDP_TAG("core.gateway.rpc")
/* Security Verification Trailer Signature */
rpc_sec_verification_trailer RPC_SEC_VERIFICATION_TRAILER =
2014-08-19 20:26:39 +04:00
{ { 0x8a, 0xe3, 0x13, 0x71, 0x02, 0xf4, 0x36, 0x71 } };
static char* PTYPE_STRINGS[] =
{
"PTYPE_REQUEST",
"PTYPE_PING",
"PTYPE_RESPONSE",
"PTYPE_FAULT",
"PTYPE_WORKING",
"PTYPE_NOCALL",
"PTYPE_REJECT",
"PTYPE_ACK",
"PTYPE_CL_CANCEL",
"PTYPE_FACK",
"PTYPE_CANCEL_ACK",
"PTYPE_BIND",
"PTYPE_BIND_ACK",
"PTYPE_BIND_NAK",
"PTYPE_ALTER_CONTEXT",
"PTYPE_ALTER_CONTEXT_RESP",
"PTYPE_RPC_AUTH_3",
"PTYPE_SHUTDOWN",
"PTYPE_CO_CANCEL",
"PTYPE_ORPHANED",
"PTYPE_RTS",
""
};
const RPC_SECURITY_PROVIDER_INFO RPC_SECURITY_PROVIDER_INFO_TABLE[] =
{
{ RPC_C_AUTHN_NONE, TRUE, -1 },
{ RPC_C_AUTHN_GSS_NEGOTIATE, TRUE, -1 },
{ RPC_C_AUTHN_WINNT, FALSE, 3 },
{ RPC_C_AUTHN_GSS_SCHANNEL, TRUE, -1 },
{ RPC_C_AUTHN_GSS_KERBEROS, TRUE, -1 },
{ RPC_C_AUTHN_DEFAULT, -1, -1 },
{ 0, -1, -1 }
};
/**
* [MS-RPCH]: Remote Procedure Call over HTTP Protocol Specification:
* http://msdn.microsoft.com/en-us/library/cc243950/
*/
/**
* 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
*
*/
BOOL rpc_connect(rdpRpc* rpc)
{
RpcInChannel* inChannel;
RpcOutChannel* outChannel;
inChannel = rpc->VirtualConnection->DefaultInChannel;
outChannel = rpc->VirtualConnection->DefaultOutChannel;
rpc_client_virtual_connection_transition_to_state(rpc, rpc->VirtualConnection, VIRTUAL_CONNECTION_STATE_INITIAL);
/* Connect IN Channel */
rpc_client_in_channel_transition_to_state(inChannel, CLIENT_IN_CHANNEL_STATE_CONNECTED);
if (rpc_ncacn_http_ntlm_init(rpc, TSG_CHANNEL_IN) < 0)
return FALSE;
/* Send IN Channel Request */
if (rpc_ncacn_http_send_in_channel_request(rpc, inChannel) < 0)
{
WLog_ERR(TAG, "rpc_ncacn_http_send_in_channel_request failure");
return FALSE;
}
rpc_client_in_channel_transition_to_state(inChannel, CLIENT_IN_CHANNEL_STATE_SECURITY);
/* Connect OUT Channel */
rpc_client_out_channel_transition_to_state(outChannel, CLIENT_OUT_CHANNEL_STATE_CONNECTED);
if (rpc_ncacn_http_ntlm_init(rpc, TSG_CHANNEL_OUT) < 0)
return FALSE;
/* Send OUT Channel Request */
if (rpc_ncacn_http_send_out_channel_request(rpc, outChannel) < 0)
{
WLog_ERR(TAG, "rpc_ncacn_http_send_out_channel_request failure");
return FALSE;
}
rpc_client_out_channel_transition_to_state(outChannel, CLIENT_OUT_CHANNEL_STATE_SECURITY);
return TRUE;
}
void rpc_pdu_header_print(rpcconn_hdr_t* header)
{
WLog_INFO(TAG, "rpc_vers: %d", header->common.rpc_vers);
WLog_INFO(TAG, "rpc_vers_minor: %d", header->common.rpc_vers_minor);
if (header->common.ptype > PTYPE_RTS)
WLog_INFO(TAG, "ptype: %s (%d)", "PTYPE_UNKNOWN", header->common.ptype);
else
WLog_INFO(TAG, "ptype: %s (%d)", PTYPE_STRINGS[header->common.ptype], header->common.ptype);
2014-08-19 20:26:39 +04:00
WLog_INFO(TAG, "pfc_flags (0x%02X) = {", header->common.pfc_flags);
if (header->common.pfc_flags & PFC_FIRST_FRAG)
WLog_INFO(TAG, " PFC_FIRST_FRAG");
2014-08-19 20:26:39 +04:00
if (header->common.pfc_flags & PFC_LAST_FRAG)
WLog_INFO(TAG, " PFC_LAST_FRAG");
2014-08-19 20:26:39 +04:00
if (header->common.pfc_flags & PFC_PENDING_CANCEL)
WLog_INFO(TAG, " PFC_PENDING_CANCEL");
2014-08-19 20:26:39 +04:00
if (header->common.pfc_flags & PFC_RESERVED_1)
WLog_INFO(TAG, " PFC_RESERVED_1");
2014-08-19 20:26:39 +04:00
if (header->common.pfc_flags & PFC_CONC_MPX)
WLog_INFO(TAG, " PFC_CONC_MPX");
2014-08-19 20:26:39 +04:00
if (header->common.pfc_flags & PFC_DID_NOT_EXECUTE)
WLog_INFO(TAG, " PFC_DID_NOT_EXECUTE");
2014-08-19 20:26:39 +04:00
if (header->common.pfc_flags & PFC_OBJECT_UUID)
WLog_INFO(TAG, " PFC_OBJECT_UUID");
WLog_INFO(TAG, " }");
WLog_INFO(TAG, "packed_drep[4]: %02X %02X %02X %02X",
header->common.packed_drep[0], header->common.packed_drep[1],
header->common.packed_drep[2], header->common.packed_drep[3]);
WLog_INFO(TAG, "frag_length: %d", header->common.frag_length);
WLog_INFO(TAG, "auth_length: %d", header->common.auth_length);
WLog_INFO(TAG, "call_id: %d", header->common.call_id);
if (header->common.ptype == PTYPE_RESPONSE)
{
WLog_INFO(TAG, "alloc_hint: %d", header->response.alloc_hint);
WLog_INFO(TAG, "p_cont_id: %d", header->response.p_cont_id);
WLog_INFO(TAG, "cancel_count: %d", header->response.cancel_count);
WLog_INFO(TAG, "reserved: %d", header->response.reserved);
}
}
void rpc_pdu_header_init(rdpRpc* rpc, rpcconn_hdr_t* header)
2012-10-30 00:41:20 +04:00
{
header->common.rpc_vers = rpc->rpc_vers;
header->common.rpc_vers_minor = rpc->rpc_vers_minor;
header->common.packed_drep[0] = rpc->packed_drep[0];
header->common.packed_drep[1] = rpc->packed_drep[1];
header->common.packed_drep[2] = rpc->packed_drep[2];
header->common.packed_drep[3] = rpc->packed_drep[3];
2012-10-30 00:41:20 +04:00
}
UINT32 rpc_offset_align(UINT32* offset, UINT32 alignment)
{
UINT32 pad;
pad = *offset;
*offset = (*offset + alignment - 1) & ~(alignment - 1);
pad = *offset - pad;
return pad;
}
UINT32 rpc_offset_pad(UINT32* offset, UINT32 pad)
{
*offset += pad;
return pad;
}
/**
* PDU Segments:
* ________________________________
* | |
* | PDU Header |
* |________________________________|
* | |
* | |
* | PDU Body |
* | |
* |________________________________|
* | |
* | Security Trailer |
* |________________________________|
* | |
* | Authentication Token |
* |________________________________|
*/
/**
* PDU Structure with verification trailer
*
* MUST only appear in a request PDU!
* ________________________________
* | |
* | PDU Header |
* |________________________________| _______
* | | /|\
* | | |
* | Stub Data | |
* | | |
* |________________________________| |
* | | PDU Body
* | Stub Pad | |
* |________________________________| |
* | | |
* | Verification Trailer | |
* |________________________________| |
* | | |
* | Authentication Pad | |
* |________________________________| __\|/__
* | |
* | Security Trailer |
* |________________________________|
* | |
* | Authentication Token |
* |________________________________|
*
*/
/**
* Security Trailer:
*
* The sec_trailer structure MUST be placed at the end of the PDU, including past stub data,
* when present. The sec_trailer structure MUST be 4-byte aligned with respect to the beginning
* of the PDU. Padding octets MUST be used to align the sec_trailer structure if its natural
* beginning is not already 4-byte aligned.
*
* All PDUs that carry sec_trailer information share certain common fields:
* frag_length and auth_length. The beginning of the sec_trailer structure for each PDU MUST be
* calculated to start from offset (frag_length auth_length 8) from the beginning of the PDU.
*
* Immediately after the sec_trailer structure, there MUST be a BLOB carrying the authentication
* information produced by the security provider. This BLOB is called the authentication token and
* MUST be of size auth_length. The size MUST also be equal to the length from the first octet
* immediately after the sec_trailer structure all the way to the end of the fragment;
* the two values MUST be the same.
*
* A client or a server that (during composing of a PDU) has allocated more space for the
* authentication token than the security provider fills in SHOULD fill in the rest of
* the allocated space with zero octets. These zero octets are still considered to belong
* to the authentication token part of the PDU.
*
*/
BOOL rpc_get_stub_data_info(rdpRpc* rpc, BYTE* buffer, UINT32* offset, UINT32* length)
{
UINT32 alloc_hint = 0;
rpcconn_hdr_t* header;
UINT32 frag_length;
UINT32 auth_length;
UINT32 auth_pad_length;
UINT32 sec_trailer_offset;
rpc_sec_trailer* sec_trailer;
*offset = RPC_COMMON_FIELDS_LENGTH;
header = ((rpcconn_hdr_t*) buffer);
switch (header->common.ptype)
{
case PTYPE_RESPONSE:
*offset += 8;
rpc_offset_align(offset, 8);
alloc_hint = header->response.alloc_hint;
break;
case PTYPE_REQUEST:
*offset += 4;
rpc_offset_align(offset, 8);
alloc_hint = header->request.alloc_hint;
break;
case PTYPE_RTS:
*offset += 4;
break;
default:
WLog_ERR(TAG, "Unknown PTYPE: 0x%04X", header->common.ptype);
return FALSE;
}
if (!length)
return TRUE;
if (header->common.ptype == PTYPE_REQUEST)
{
UINT32 sec_trailer_offset;
sec_trailer_offset = header->common.frag_length - header->common.auth_length - 8;
*length = sec_trailer_offset - *offset;
return TRUE;
}
frag_length = header->common.frag_length;
auth_length = header->common.auth_length;
sec_trailer_offset = frag_length - auth_length - 8;
sec_trailer = (rpc_sec_trailer*) &buffer[sec_trailer_offset];
auth_pad_length = sec_trailer->auth_pad_length;
#if 0
WLog_DBG(TAG, "sec_trailer: type: %d level: %d pad_length: %d reserved: %d context_id: %d",
sec_trailer->auth_type, sec_trailer->auth_level,
sec_trailer->auth_pad_length, sec_trailer->auth_reserved,
sec_trailer->auth_context_id);
#endif
/**
* According to [MS-RPCE], auth_pad_length is the number of padding
* octets used to 4-byte align the security trailer, but in practice
* we get values up to 15, which indicates 16-byte alignment.
*/
if ((frag_length - (sec_trailer_offset + 8)) != auth_length)
{
WLog_ERR(TAG, "invalid auth_length: actual: %d, expected: %d", auth_length,
(frag_length - (sec_trailer_offset + 8)));
}
*length = frag_length - auth_length - 24 - 8 - auth_pad_length;
return TRUE;
}
2012-11-17 12:45:15 +04:00
int rpc_out_read(rdpRpc* rpc, BYTE* data, int length)
{
int status;
RpcOutChannel* outChannel = rpc->VirtualConnection->DefaultOutChannel;
status = BIO_read(outChannel->tls->bio, data, length);
2014-08-19 20:26:39 +04:00
if (status > 0)
{
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_DEFINED(data, status);
#endif
return status;
}
2012-11-17 12:45:15 +04:00
if (BIO_should_retry(outChannel->tls->bio))
return 0;
return -1;
2012-11-17 12:45:15 +04:00
}
int rpc_out_write(rdpRpc* rpc, const BYTE* data, int length)
2012-11-17 12:45:15 +04:00
{
int status;
RpcOutChannel* outChannel = rpc->VirtualConnection->DefaultOutChannel;
status = tls_write_all(outChannel->tls, data, length);
2012-11-17 12:45:15 +04:00
return status;
}
int rpc_in_write(rdpRpc* rpc, const BYTE* data, int length)
2012-11-17 12:45:15 +04:00
{
int status;
RpcInChannel* inChannel = rpc->VirtualConnection->DefaultInChannel;
status = tls_write_all(inChannel->tls, data, length);
2012-11-17 12:45:15 +04:00
return status;
}
int rpc_write(rdpRpc* rpc, BYTE* data, int length, UINT16 opnum)
{
UINT32 offset;
BYTE* buffer = NULL;
UINT32 stub_data_pad;
SecBuffer Buffers[2];
SecBufferDesc Message;
RpcClientCall* clientCall;
SECURITY_STATUS encrypt_status;
2014-11-17 01:06:03 +03:00
rpcconn_request_hdr_t* request_pdu = NULL;
rdpNtlm* ntlm = rpc->ntlm;
if (!ntlm || !ntlm->table)
{
WLog_ERR(TAG, "invalid ntlm context");
return -1;
}
if (ntlm->table->QueryContextAttributes(&ntlm->context, SECPKG_ATTR_SIZES, &ntlm->ContextSizes) != SEC_E_OK)
{
WLog_ERR(TAG, "QueryContextAttributes SECPKG_ATTR_SIZES failure");
return -1;
}
ZeroMemory(&Buffers, sizeof(Buffers));
request_pdu = (rpcconn_request_hdr_t*) calloc(1, sizeof(rpcconn_request_hdr_t));
2014-08-19 20:26:39 +04:00
if (!request_pdu)
return -1;
2012-10-30 00:41:20 +04:00
rpc_pdu_header_init(rpc, (rpcconn_hdr_t*) request_pdu);
request_pdu->ptype = PTYPE_REQUEST;
request_pdu->pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG;
2014-02-11 07:12:13 +04:00
request_pdu->auth_length = (UINT16) ntlm->ContextSizes.cbMaxSignature;
2012-12-12 08:34:51 +04:00
request_pdu->call_id = rpc->CallId++;
request_pdu->alloc_hint = length;
request_pdu->p_cont_id = 0x0000;
request_pdu->opnum = opnum;
clientCall = rpc_client_call_new(request_pdu->call_id, request_pdu->opnum);
2014-08-19 20:26:39 +04:00
if (!clientCall)
goto out_free_pdu;
if (ArrayList_Add(rpc->client->ClientCallList, clientCall) < 0)
goto out_free_clientCall;
if (request_pdu->opnum == TsProxySetupReceivePipeOpnum)
rpc->PipeCallId = request_pdu->call_id;
request_pdu->stub_data = data;
offset = 24;
stub_data_pad = 0;
stub_data_pad = rpc_offset_align(&offset, 8);
offset += length;
request_pdu->auth_verifier.auth_pad_length = rpc_offset_align(&offset, 4);
request_pdu->auth_verifier.auth_type = RPC_C_AUTHN_WINNT;
request_pdu->auth_verifier.auth_level = RPC_C_AUTHN_LEVEL_PKT_INTEGRITY;
request_pdu->auth_verifier.auth_reserved = 0x00;
request_pdu->auth_verifier.auth_context_id = 0x00000000;
offset += (8 + request_pdu->auth_length);
request_pdu->frag_length = offset;
buffer = (BYTE*) calloc(1, request_pdu->frag_length);
2014-08-19 20:26:39 +04:00
if (!buffer)
goto out_free_pdu;
2014-08-19 20:26:39 +04:00
CopyMemory(buffer, request_pdu, 24);
offset = 24;
rpc_offset_pad(&offset, stub_data_pad);
CopyMemory(&buffer[offset], request_pdu->stub_data, length);
offset += length;
rpc_offset_pad(&offset, request_pdu->auth_verifier.auth_pad_length);
CopyMemory(&buffer[offset], &request_pdu->auth_verifier.auth_type, 8);
offset += 8;
Buffers[0].BufferType = SECBUFFER_DATA; /* auth_data */
Buffers[1].BufferType = SECBUFFER_TOKEN; /* signature */
Buffers[0].pvBuffer = buffer;
Buffers[0].cbBuffer = offset;
Buffers[1].cbBuffer = ntlm->ContextSizes.cbMaxSignature;
Buffers[1].pvBuffer = calloc(1, Buffers[1].cbBuffer);
2014-08-19 20:26:39 +04:00
if (!Buffers[1].pvBuffer)
2014-11-17 01:06:03 +03:00
goto out_free_pdu;
Message.cBuffers = 2;
Message.ulVersion = SECBUFFER_VERSION;
Message.pBuffers = (PSecBuffer) &Buffers;
2012-12-12 08:34:51 +04:00
encrypt_status = ntlm->table->EncryptMessage(&ntlm->context, 0, &Message, rpc->SendSeqNum++);
2014-08-19 20:26:39 +04:00
if (encrypt_status != SEC_E_OK)
{
WLog_ERR(TAG, "EncryptMessage status: 0x%08X", encrypt_status);
2014-11-17 01:06:03 +03:00
goto out_free_pdu;
}
CopyMemory(&buffer[offset], Buffers[1].pvBuffer, Buffers[1].cbBuffer);
offset += Buffers[1].cbBuffer;
free(Buffers[1].pvBuffer);
if (rpc_send_pdu(rpc, buffer, request_pdu->frag_length) < 0)
length = -1;
2012-12-12 08:34:51 +04:00
free(request_pdu);
free(buffer);
2012-12-12 08:34:51 +04:00
return length;
2014-11-17 01:06:03 +03:00
out_free_clientCall:
rpc_client_call_free(clientCall);
out_free_pdu:
free(buffer);
free(Buffers[1].pvBuffer);
free(request_pdu);
return -1;
}
int rpc_client_in_channel_transition_to_state(RpcInChannel* inChannel, CLIENT_IN_CHANNEL_STATE state)
{
int status = 1;
const char* str = "CLIENT_IN_CHANNEL_STATE_UNKNOWN";
switch (state)
{
case CLIENT_IN_CHANNEL_STATE_INITIAL:
str = "CLIENT_IN_CHANNEL_STATE_INITIAL";
break;
case CLIENT_IN_CHANNEL_STATE_CONNECTED:
str = "CLIENT_IN_CHANNEL_STATE_CONNECTED";
break;
case CLIENT_IN_CHANNEL_STATE_SECURITY:
str = "CLIENT_IN_CHANNEL_STATE_SECURITY";
break;
case CLIENT_IN_CHANNEL_STATE_NEGOTIATED:
str = "CLIENT_IN_CHANNEL_STATE_NEGOTIATED";
break;
case CLIENT_IN_CHANNEL_STATE_OPENED:
str = "CLIENT_IN_CHANNEL_STATE_OPENED";
break;
case CLIENT_IN_CHANNEL_STATE_OPENED_A4W:
str = "CLIENT_IN_CHANNEL_STATE_OPENED_A4W";
break;
case CLIENT_IN_CHANNEL_STATE_FINAL:
str = "CLIENT_IN_CHANNEL_STATE_FINAL";
break;
}
inChannel->State = state;
WLog_DBG(TAG, "%s", str);
return status;
}
int rpc_client_in_channel_rpch_init(rdpRpc* rpc, RpcInChannel* inChannel)
{
inChannel->ntlm = ntlm_new();
if (!inChannel->ntlm)
return -1;
inChannel->http = http_context_new();
if (!inChannel->http)
return -1;
rpc_ntlm_http_init_channel(rpc, inChannel->http, TSG_CHANNEL_IN);
return 1;
}
void rpc_client_in_channel_rpch_uninit(RpcInChannel* inChannel)
{
if (inChannel->ntlm)
{
ntlm_free(inChannel->ntlm);
inChannel->ntlm = NULL;
}
if (inChannel->http)
{
http_context_free(inChannel->http);
inChannel->http = NULL;
}
}
RpcInChannel* rpc_client_in_channel_new(rdpRpc* rpc)
{
RpcInChannel* inChannel = NULL;
inChannel = (RpcInChannel*) calloc(1, sizeof(RpcInChannel));
if (inChannel)
{
}
return inChannel;
}
void rpc_client_in_channel_free(RpcInChannel* inChannel)
{
if (!inChannel)
return;
rpc_client_in_channel_rpch_uninit(inChannel);
free(inChannel);
}
int rpc_client_out_channel_transition_to_state(RpcOutChannel* outChannel, CLIENT_OUT_CHANNEL_STATE state)
{
int status = 1;
const char* str = "CLIENT_OUT_CHANNEL_STATE_UNKNOWN";
switch (state)
{
case CLIENT_OUT_CHANNEL_STATE_INITIAL:
str = "CLIENT_OUT_CHANNEL_STATE_INITIAL";
break;
case CLIENT_OUT_CHANNEL_STATE_CONNECTED:
str = "CLIENT_OUT_CHANNEL_STATE_CONNECTED";
break;
case CLIENT_OUT_CHANNEL_STATE_SECURITY:
str = "CLIENT_OUT_CHANNEL_STATE_SECURITY";
break;
case CLIENT_OUT_CHANNEL_STATE_NEGOTIATED:
str = "CLIENT_OUT_CHANNEL_STATE_NEGOTIATED";
break;
case CLIENT_OUT_CHANNEL_STATE_OPENED:
str = "CLIENT_OUT_CHANNEL_STATE_OPENED";
break;
case CLIENT_OUT_CHANNEL_STATE_OPENED_A6W:
str = "CLIENT_OUT_CHANNEL_STATE_OPENED_A6W";
break;
case CLIENT_OUT_CHANNEL_STATE_OPENED_A10W:
str = "CLIENT_OUT_CHANNEL_STATE_OPENED_A10W";
break;
case CLIENT_OUT_CHANNEL_STATE_OPENED_B3W:
str = "CLIENT_OUT_CHANNEL_STATE_OPENED_B3W";
break;
case CLIENT_OUT_CHANNEL_STATE_FINAL:
str = "CLIENT_OUT_CHANNEL_STATE_FINAL";
break;
}
outChannel->State = state;
WLog_DBG(TAG, "%s", str);
return status;
}
int rpc_client_out_channel_rpch_init(rdpRpc* rpc, RpcOutChannel* outChannel)
{
outChannel->ntlm = ntlm_new();
if (!outChannel->ntlm)
return -1;
outChannel->http = http_context_new();
if (!outChannel->http)
return -1;
rpc_ntlm_http_init_channel(rpc, outChannel->http, TSG_CHANNEL_OUT);
return 1;
}
void rpc_client_out_channel_rpch_uninit(RpcOutChannel* outChannel)
{
if (outChannel->ntlm)
{
ntlm_free(outChannel->ntlm);
outChannel->ntlm = NULL;
}
if (outChannel->http)
{
http_context_free(outChannel->http);
outChannel->http = NULL;
}
}
RpcOutChannel* rpc_client_out_channel_new(rdpRpc* rpc)
{
RpcOutChannel* outChannel = NULL;
outChannel = (RpcOutChannel*) calloc(1, sizeof(RpcOutChannel));
if (outChannel)
{
}
return outChannel;
}
void rpc_client_out_channel_free(RpcOutChannel* outChannel)
{
if (!outChannel)
return;
rpc_client_out_channel_rpch_uninit(outChannel);
free(outChannel);
}
int rpc_client_virtual_connection_transition_to_state(rdpRpc* rpc,
RpcVirtualConnection* connection, VIRTUAL_CONNECTION_STATE state)
{
int status = 1;
const char* str = "VIRTUAL_CONNECTION_STATE_UNKNOWN";
switch (state)
{
case VIRTUAL_CONNECTION_STATE_INITIAL:
str = "VIRTUAL_CONNECTION_STATE_INITIAL";
break;
case VIRTUAL_CONNECTION_STATE_OUT_CHANNEL_WAIT:
str = "VIRTUAL_CONNECTION_STATE_OUT_CHANNEL_WAIT";
break;
case VIRTUAL_CONNECTION_STATE_WAIT_A3W:
str = "VIRTUAL_CONNECTION_STATE_WAIT_A3W";
break;
case VIRTUAL_CONNECTION_STATE_WAIT_C2:
str = "VIRTUAL_CONNECTION_STATE_WAIT_C2";
break;
case VIRTUAL_CONNECTION_STATE_OPENED:
str = "VIRTUAL_CONNECTION_STATE_OPENED";
break;
case VIRTUAL_CONNECTION_STATE_FINAL:
str = "VIRTUAL_CONNECTION_STATE_FINAL";
break;
}
connection->State = state;
WLog_DBG(TAG, "%s", str);
return status;
}
void rpc_client_virtual_connection_init(rdpRpc* rpc, RpcVirtualConnection* connection)
{
rts_generate_cookie((BYTE*) &(connection->Cookie));
rts_generate_cookie((BYTE*) &(connection->AssociationGroupId));
connection->DefaultInChannel->State = CLIENT_IN_CHANNEL_STATE_INITIAL;
connection->DefaultInChannel->BytesSent = 0;
connection->DefaultInChannel->SenderAvailableWindow = rpc->ReceiveWindow;
connection->DefaultInChannel->PingOriginator.ConnectionTimeout = 30;
connection->DefaultInChannel->PingOriginator.KeepAliveInterval = 0;
rts_generate_cookie((BYTE*) &(connection->DefaultInChannelCookie));
rts_generate_cookie((BYTE*) &(connection->NonDefaultInChannelCookie));
rpc_client_in_channel_rpch_init(rpc, connection->DefaultInChannel);
connection->DefaultOutChannel->State = CLIENT_OUT_CHANNEL_STATE_INITIAL;
connection->DefaultOutChannel->BytesReceived = 0;
connection->DefaultOutChannel->ReceiverAvailableWindow = rpc->ReceiveWindow;
connection->DefaultOutChannel->ReceiveWindow = rpc->ReceiveWindow;
connection->DefaultOutChannel->ReceiveWindowSize = rpc->ReceiveWindow;
connection->DefaultOutChannel->AvailableWindowAdvertised = rpc->ReceiveWindow;
rts_generate_cookie((BYTE*) &(connection->DefaultOutChannelCookie));
rts_generate_cookie((BYTE*) &(connection->NonDefaultOutChannelCookie));
rpc_client_out_channel_rpch_init(rpc, connection->DefaultOutChannel);
}
RpcVirtualConnection* rpc_client_virtual_connection_new(rdpRpc* rpc)
{
RpcVirtualConnection* connection;
connection = (RpcVirtualConnection*) calloc(1, sizeof(RpcVirtualConnection));
2014-04-10 23:07:53 +04:00
if (!connection)
return NULL;
connection->State = VIRTUAL_CONNECTION_STATE_INITIAL;
connection->DefaultInChannel = rpc_client_in_channel_new(rpc);
2014-08-19 20:26:39 +04:00
2014-04-10 23:07:53 +04:00
if (!connection->DefaultInChannel)
goto out_free;
2014-08-19 20:26:39 +04:00
connection->DefaultOutChannel = rpc_client_out_channel_new(rpc);
2014-08-19 20:26:39 +04:00
2014-04-10 23:07:53 +04:00
if (!connection->DefaultOutChannel)
goto out_default_in;
2014-08-19 20:26:39 +04:00
rpc_client_virtual_connection_init(rpc, connection);
return connection;
2014-04-10 23:07:53 +04:00
out_default_in:
free(connection->DefaultInChannel);
out_free:
free(connection);
return NULL;
}
void rpc_client_virtual_connection_free(RpcVirtualConnection* virtualConnection)
{
if (!virtualConnection)
return;
rpc_client_in_channel_free(virtualConnection->DefaultInChannel);
rpc_client_out_channel_free(virtualConnection->DefaultOutChannel);
free(virtualConnection);
}
rdpRpc* rpc_new(rdpTransport* transport)
{
2014-04-10 23:07:53 +04:00
rdpRpc* rpc = (rdpRpc*) calloc(1, sizeof(rdpRpc));
2014-08-19 20:26:39 +04:00
2014-04-10 23:07:53 +04:00
if (!rpc)
return NULL;
2014-04-10 23:07:53 +04:00
rpc->State = RPC_CLIENT_STATE_INITIAL;
2014-04-10 23:07:53 +04:00
rpc->transport = transport;
rpc->settings = transport->settings;
rpc->context = transport->context;
2014-04-10 23:07:53 +04:00
rpc->SendSeqNum = 0;
2014-04-10 23:07:53 +04:00
rpc->ntlm = ntlm_new();
2014-08-19 20:26:39 +04:00
2014-04-10 23:07:53 +04:00
if (!rpc->ntlm)
goto out_free;
2014-04-10 23:07:53 +04:00
rpc->PipeCallId = 0;
rpc->StubCallId = 0;
rpc->StubFragCount = 0;
rpc->rpc_vers = 5;
rpc->rpc_vers_minor = 0;
2014-04-10 23:07:53 +04:00
/* little-endian data representation */
rpc->packed_drep[0] = 0x10;
rpc->packed_drep[1] = 0x00;
rpc->packed_drep[2] = 0x00;
rpc->packed_drep[3] = 0x00;
rpc->max_xmit_frag = 0x0FF8;
rpc->max_recv_frag = 0x0FF8;
2014-04-10 23:07:53 +04:00
rpc->ReceiveWindow = 0x00010000;
rpc->ChannelLifetime = 0x40000000;
rpc->KeepAliveInterval = 300000;
rpc->CurrentKeepAliveInterval = rpc->KeepAliveInterval;
rpc->CurrentKeepAliveTime = 0;
2014-04-10 23:07:53 +04:00
rpc->VirtualConnection = rpc_client_virtual_connection_new(rpc);
2014-08-19 20:26:39 +04:00
2014-04-10 23:07:53 +04:00
if (!rpc->VirtualConnection)
goto out_free_virtual_connection;
2014-04-10 23:07:53 +04:00
rpc->CallId = 2;
if (rpc_client_new(rpc) < 0)
goto out_free_rpc_client;
2012-04-18 09:53:12 +04:00
return rpc;
out_free_rpc_client:
rpc_client_free(rpc);
2014-04-10 23:07:53 +04:00
out_free_virtual_connection:
rpc_client_virtual_connection_free(rpc->VirtualConnection);
out_free:
free(rpc);
return NULL;
}
void rpc_free(rdpRpc* rpc)
{
if (rpc)
{
rpc_client_free(rpc);
if (rpc->ntlm)
{
ntlm_client_uninit(rpc->ntlm);
ntlm_free(rpc->ntlm);
rpc->ntlm = NULL;
}
rpc_client_virtual_connection_free(rpc->VirtualConnection);
free(rpc);
}
}