FreeRDP/libfreerdp/core/gateway/rpc.c

941 lines
25 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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
#include <winpr/crt.h>
#include <winpr/tchar.h>
#include <winpr/synch.h>
#include <winpr/dsparse.h>
#include <freerdp/log.h>
#include <openssl/rand.h>
#include <openssl/bio.h>
#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#endif
#include "http.h"
#include "ntlm.h"
#include "ncacn_http.h"
#include "rpc_bind.h"
#include "rpc_fault.h"
#include "rpc_client.h"
#include "rpc.h"
#define TAG FREERDP_TAG("core.gateway.rpc")
/* Security Verification Trailer Signature */
rpc_sec_verification_trailer RPC_SEC_VERIFICATION_TRAILER =
{ { 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);
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");
if (header->common.pfc_flags & PFC_LAST_FRAG)
WLog_INFO(TAG, " PFC_LAST_FRAG");
if (header->common.pfc_flags & PFC_PENDING_CANCEL)
WLog_INFO(TAG, " PFC_PENDING_CANCEL");
if (header->common.pfc_flags & PFC_RESERVED_1)
WLog_INFO(TAG, " PFC_RESERVED_1");
if (header->common.pfc_flags & PFC_CONC_MPX)
WLog_INFO(TAG, " PFC_CONC_MPX");
if (header->common.pfc_flags & PFC_DID_NOT_EXECUTE)
WLog_INFO(TAG, " PFC_DID_NOT_EXECUTE");
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)
{
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];
}
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;
}
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);
if (status > 0)
{
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_DEFINED(data, status);
#endif
return status;
}
if (BIO_should_retry(outChannel->tls->bio))
return 0;
return -1;
}
int rpc_out_write(rdpRpc* rpc, const BYTE* data, int length)
{
int status;
RpcOutChannel* outChannel = rpc->VirtualConnection->DefaultOutChannel;
status = tls_write_all(outChannel->tls, data, length);
return status;
}
int rpc_in_write(rdpRpc* rpc, const BYTE* data, int length)
{
int status;
RpcInChannel* inChannel = rpc->VirtualConnection->DefaultInChannel;
status = tls_write_all(inChannel->tls, data, length);
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;
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));
if (!request_pdu)
return -1;
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;
request_pdu->auth_length = (UINT16) ntlm->ContextSizes.cbMaxSignature;
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);
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);
if (!buffer)
goto out_free_pdu;
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);
if (!Buffers[1].pvBuffer)
goto out_free_pdu;
Message.cBuffers = 2;
Message.ulVersion = SECBUFFER_VERSION;
Message.pBuffers = (PSecBuffer) &Buffers;
encrypt_status = ntlm->table->EncryptMessage(&ntlm->context, 0, &Message, rpc->SendSeqNum++);
if (encrypt_status != SEC_E_OK)
{
WLog_ERR(TAG, "EncryptMessage status: 0x%08X", encrypt_status);
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;
free(request_pdu);
free(buffer);
return length;
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));
if (!connection)
return NULL;
connection->State = VIRTUAL_CONNECTION_STATE_INITIAL;
connection->DefaultInChannel = rpc_client_in_channel_new(rpc);
if (!connection->DefaultInChannel)
goto out_free;
connection->DefaultOutChannel = rpc_client_out_channel_new(rpc);
if (!connection->DefaultOutChannel)
goto out_default_in;
rpc_client_virtual_connection_init(rpc, connection);
return connection;
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)
{
rdpRpc* rpc = (rdpRpc*) calloc(1, sizeof(rdpRpc));
if (!rpc)
return NULL;
rpc->State = RPC_CLIENT_STATE_INITIAL;
rpc->transport = transport;
rpc->settings = transport->settings;
rpc->context = transport->context;
rpc->SendSeqNum = 0;
rpc->ntlm = ntlm_new();
if (!rpc->ntlm)
goto out_free;
rpc->PipeCallId = 0;
rpc->StubCallId = 0;
rpc->StubFragCount = 0;
rpc->rpc_vers = 5;
rpc->rpc_vers_minor = 0;
/* 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;
rpc->ReceiveWindow = 0x00010000;
rpc->ChannelLifetime = 0x40000000;
rpc->KeepAliveInterval = 300000;
rpc->CurrentKeepAliveInterval = rpc->KeepAliveInterval;
rpc->CurrentKeepAliveTime = 0;
rpc->VirtualConnection = rpc_client_virtual_connection_new(rpc);
if (!rpc->VirtualConnection)
goto out_free_virtual_connection;
rpc->CallId = 2;
if (rpc_client_new(rpc) < 0)
goto out_free_rpc_client;
return rpc;
out_free_rpc_client:
rpc_client_free(rpc);
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);
}
}