FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
libfreerdp/core/gateway/rpc.c
1/*
2 * FreeRDP: A Remote Desktop Protocol Implementation
3 * RPC over HTTP
4 *
5 * Copyright 2012 Fujitsu Technology Solutions GmbH
6 * Copyright 2012 Dmitrij Jasnov <dmitrij.jasnov@ts.fujitsu.com>
7 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
8 *
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 */
21
22#include <freerdp/config.h>
23
24#include "../settings.h"
25
26#include <winpr/crt.h>
27#include <winpr/assert.h>
28#include <winpr/cast.h>
29#include <winpr/tchar.h>
30#include <winpr/synch.h>
31#include <winpr/dsparse.h>
32#include <winpr/crypto.h>
33
34#include <freerdp/log.h>
35
36#ifdef FREERDP_HAVE_VALGRIND_MEMCHECK_H
37#include <valgrind/memcheck.h>
38#endif
39
40#include "../proxy.h"
41#include "http.h"
42#include "../credssp_auth.h"
43#include "ncacn_http.h"
44#include "rpc_bind.h"
45#include "rpc_fault.h"
46#include "rpc_client.h"
47
48#include "rpc.h"
49#include "rts.h"
50
51#define TAG FREERDP_TAG("core.gateway.rpc")
52
53static const char* PTYPE_STRINGS[] = { "PTYPE_REQUEST", "PTYPE_PING",
54 "PTYPE_RESPONSE", "PTYPE_FAULT",
55 "PTYPE_WORKING", "PTYPE_NOCALL",
56 "PTYPE_REJECT", "PTYPE_ACK",
57 "PTYPE_CL_CANCEL", "PTYPE_FACK",
58 "PTYPE_CANCEL_ACK", "PTYPE_BIND",
59 "PTYPE_BIND_ACK", "PTYPE_BIND_NAK",
60 "PTYPE_ALTER_CONTEXT", "PTYPE_ALTER_CONTEXT_RESP",
61 "PTYPE_RPC_AUTH_3", "PTYPE_SHUTDOWN",
62 "PTYPE_CO_CANCEL", "PTYPE_ORPHANED",
63 "PTYPE_RTS", "" };
64
65static const char* client_in_state_str(CLIENT_IN_CHANNEL_STATE state)
66{
67 // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
68 const char* str = "CLIENT_IN_CHANNEL_STATE_UNKNOWN";
69
70 switch (state)
71 {
72 case CLIENT_IN_CHANNEL_STATE_INITIAL:
73 str = "CLIENT_IN_CHANNEL_STATE_INITIAL";
74 break;
75
76 case CLIENT_IN_CHANNEL_STATE_CONNECTED:
77 str = "CLIENT_IN_CHANNEL_STATE_CONNECTED";
78 break;
79
80 case CLIENT_IN_CHANNEL_STATE_SECURITY:
81 str = "CLIENT_IN_CHANNEL_STATE_SECURITY";
82 break;
83
84 case CLIENT_IN_CHANNEL_STATE_NEGOTIATED:
85 str = "CLIENT_IN_CHANNEL_STATE_NEGOTIATED";
86 break;
87
88 case CLIENT_IN_CHANNEL_STATE_OPENED:
89 str = "CLIENT_IN_CHANNEL_STATE_OPENED";
90 break;
91
92 case CLIENT_IN_CHANNEL_STATE_OPENED_A4W:
93 str = "CLIENT_IN_CHANNEL_STATE_OPENED_A4W";
94 break;
95
96 case CLIENT_IN_CHANNEL_STATE_FINAL:
97 str = "CLIENT_IN_CHANNEL_STATE_FINAL";
98 break;
99 default:
100 break;
101 }
102 return str;
103}
104
105static const char* client_out_state_str(CLIENT_OUT_CHANNEL_STATE state)
106{
107 // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
108 const char* str = "CLIENT_OUT_CHANNEL_STATE_UNKNOWN";
109
110 switch (state)
111 {
112 case CLIENT_OUT_CHANNEL_STATE_INITIAL:
113 str = "CLIENT_OUT_CHANNEL_STATE_INITIAL";
114 break;
115
116 case CLIENT_OUT_CHANNEL_STATE_CONNECTED:
117 str = "CLIENT_OUT_CHANNEL_STATE_CONNECTED";
118 break;
119
120 case CLIENT_OUT_CHANNEL_STATE_SECURITY:
121 str = "CLIENT_OUT_CHANNEL_STATE_SECURITY";
122 break;
123
124 case CLIENT_OUT_CHANNEL_STATE_NEGOTIATED:
125 str = "CLIENT_OUT_CHANNEL_STATE_NEGOTIATED";
126 break;
127
128 case CLIENT_OUT_CHANNEL_STATE_OPENED:
129 str = "CLIENT_OUT_CHANNEL_STATE_OPENED";
130 break;
131
132 case CLIENT_OUT_CHANNEL_STATE_OPENED_A6W:
133 str = "CLIENT_OUT_CHANNEL_STATE_OPENED_A6W";
134 break;
135
136 case CLIENT_OUT_CHANNEL_STATE_OPENED_A10W:
137 str = "CLIENT_OUT_CHANNEL_STATE_OPENED_A10W";
138 break;
139
140 case CLIENT_OUT_CHANNEL_STATE_OPENED_B3W:
141 str = "CLIENT_OUT_CHANNEL_STATE_OPENED_B3W";
142 break;
143
144 case CLIENT_OUT_CHANNEL_STATE_RECYCLED:
145 str = "CLIENT_OUT_CHANNEL_STATE_RECYCLED";
146 break;
147
148 case CLIENT_OUT_CHANNEL_STATE_FINAL:
149 str = "CLIENT_OUT_CHANNEL_STATE_FINAL";
150 break;
151 default:
152 break;
153 }
154 return str;
155}
156
157const char* rpc_vc_state_str(VIRTUAL_CONNECTION_STATE state)
158{
159 // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
160 const char* str = "VIRTUAL_CONNECTION_STATE_UNKNOWN";
161
162 switch (state)
163 {
164 case VIRTUAL_CONNECTION_STATE_INITIAL:
165 str = "VIRTUAL_CONNECTION_STATE_INITIAL";
166 break;
167
168 case VIRTUAL_CONNECTION_STATE_OUT_CHANNEL_WAIT:
169 str = "VIRTUAL_CONNECTION_STATE_OUT_CHANNEL_WAIT";
170 break;
171
172 case VIRTUAL_CONNECTION_STATE_WAIT_A3W:
173 str = "VIRTUAL_CONNECTION_STATE_WAIT_A3W";
174 break;
175
176 case VIRTUAL_CONNECTION_STATE_WAIT_C2:
177 str = "VIRTUAL_CONNECTION_STATE_WAIT_C2";
178 break;
179
180 case VIRTUAL_CONNECTION_STATE_OPENED:
181 str = "VIRTUAL_CONNECTION_STATE_OPENED";
182 break;
183
184 case VIRTUAL_CONNECTION_STATE_FINAL:
185 str = "VIRTUAL_CONNECTION_STATE_FINAL";
186 break;
187 default:
188 break;
189 }
190 return str;
191}
192
193/*
194 * [MS-RPCH]: Remote Procedure Call over HTTP Protocol Specification:
195 * http://msdn.microsoft.com/en-us/library/cc243950/
196 *
197 *
198 *
199 * Connection Establishment
200 *
201 * Client Outbound Proxy Inbound Proxy Server
202 * | | | |
203 * |-----------------IN Channel Request--------------->| |
204 * |---OUT Channel Request-->| |<-Legacy Server Response-|
205 * | |<--------------Legacy Server Response--------------|
206 * | | | |
207 * |---------CONN_A1-------->| | |
208 * |----------------------CONN_B1--------------------->| |
209 * | |----------------------CONN_A2--------------------->|
210 * | | | |
211 * |<--OUT Channel Response--| |---------CONN_B2-------->|
212 * |<--------CONN_A3---------| | |
213 * | |<---------------------CONN_C1----------------------|
214 * | | |<--------CONN_B3---------|
215 * |<--------CONN_C2---------| | |
216 * | | | |
217 *
218 */
219
220void rpc_pdu_header_print(wLog* log, const rpcconn_hdr_t* header)
221{
222 WINPR_ASSERT(header);
223
224 WLog_Print(log, WLOG_INFO, "rpc_vers: %" PRIu8 "", header->common.rpc_vers);
225 WLog_Print(log, WLOG_INFO, "rpc_vers_minor: %" PRIu8 "", header->common.rpc_vers_minor);
226
227 if (header->common.ptype > PTYPE_RTS)
228 WLog_Print(log, WLOG_INFO, "ptype: %s (%" PRIu8 ")", "PTYPE_UNKNOWN", header->common.ptype);
229 else
230 WLog_Print(log, WLOG_INFO, "ptype: %s (%" PRIu8 ")", PTYPE_STRINGS[header->common.ptype],
231 header->common.ptype);
232
233 WLog_Print(log, WLOG_INFO, "pfc_flags (0x%02" PRIX8 ") = {", header->common.pfc_flags);
234
235 if (header->common.pfc_flags & PFC_FIRST_FRAG)
236 WLog_Print(log, WLOG_INFO, " PFC_FIRST_FRAG");
237
238 if (header->common.pfc_flags & PFC_LAST_FRAG)
239 WLog_Print(log, WLOG_INFO, " PFC_LAST_FRAG");
240
241 if (header->common.pfc_flags & PFC_PENDING_CANCEL)
242 WLog_Print(log, WLOG_INFO, " PFC_PENDING_CANCEL");
243
244 if (header->common.pfc_flags & PFC_RESERVED_1)
245 WLog_Print(log, WLOG_INFO, " PFC_RESERVED_1");
246
247 if (header->common.pfc_flags & PFC_CONC_MPX)
248 WLog_Print(log, WLOG_INFO, " PFC_CONC_MPX");
249
250 if (header->common.pfc_flags & PFC_DID_NOT_EXECUTE)
251 WLog_Print(log, WLOG_INFO, " PFC_DID_NOT_EXECUTE");
252
253 if (header->common.pfc_flags & PFC_OBJECT_UUID)
254 WLog_Print(log, WLOG_INFO, " PFC_OBJECT_UUID");
255
256 WLog_Print(log, WLOG_INFO, " }");
257 WLog_Print(log, WLOG_INFO,
258 "packed_drep[4]: %02" PRIX8 " %02" PRIX8 " %02" PRIX8 " %02" PRIX8 "",
259 header->common.packed_drep[0], header->common.packed_drep[1],
260 header->common.packed_drep[2], header->common.packed_drep[3]);
261 WLog_Print(log, WLOG_INFO, "frag_length: %" PRIu16 "", header->common.frag_length);
262 WLog_Print(log, WLOG_INFO, "auth_length: %" PRIu16 "", header->common.auth_length);
263 WLog_Print(log, WLOG_INFO, "call_id: %" PRIu32 "", header->common.call_id);
264
265 if (header->common.ptype == PTYPE_RESPONSE)
266 {
267 WLog_Print(log, WLOG_INFO, "alloc_hint: %" PRIu32 "", header->response.alloc_hint);
268 WLog_Print(log, WLOG_INFO, "p_cont_id: %" PRIu16 "", header->response.p_cont_id);
269 WLog_Print(log, WLOG_INFO, "cancel_count: %" PRIu8 "", header->response.cancel_count);
270 WLog_Print(log, WLOG_INFO, "reserved: %" PRIu8 "", header->response.reserved);
271 }
272}
273
274rpcconn_common_hdr_t rpc_pdu_header_init(const rdpRpc* rpc)
275{
276 rpcconn_common_hdr_t header = { 0 };
277 WINPR_ASSERT(rpc);
278
279 header.rpc_vers = rpc->rpc_vers;
280 header.rpc_vers_minor = rpc->rpc_vers_minor;
281 header.packed_drep[0] = rpc->packed_drep[0];
282 header.packed_drep[1] = rpc->packed_drep[1];
283 header.packed_drep[2] = rpc->packed_drep[2];
284 header.packed_drep[3] = rpc->packed_drep[3];
285 return header;
286}
287
288size_t rpc_offset_align(size_t* offset, size_t alignment)
289{
290 size_t pad = 0;
291 pad = *offset;
292 *offset = (*offset + alignment - 1) & ~(alignment - 1);
293 pad = *offset - pad;
294 return pad;
295}
296
297size_t rpc_offset_pad(size_t* offset, size_t pad)
298{
299 *offset += pad;
300 return pad;
301}
302
303/*
304 * PDU Segments:
305 * ________________________________
306 * | |
307 * | PDU Header |
308 * |________________________________|
309 * | |
310 * | |
311 * | PDU Body |
312 * | |
313 * |________________________________|
314 * | |
315 * | Security Trailer |
316 * |________________________________|
317 * | |
318 * | Authentication Token |
319 * |________________________________|
320 */
321
322/*
323 * PDU Structure with verification trailer
324 *
325 * MUST only appear in a request PDU!
326 * ________________________________
327 * | |
328 * | PDU Header |
329 * |________________________________| _______
330 * | | /|\
331 * | | |
332 * | Stub Data | |
333 * | | |
334 * |________________________________| |
335 * | | PDU Body
336 * | Stub Pad | |
337 * |________________________________| |
338 * | | |
339 * | Verification Trailer | |
340 * |________________________________| |
341 * | | |
342 * | Authentication Pad | |
343 * |________________________________| __\|/__
344 * | |
345 * | Security Trailer |
346 * |________________________________|
347 * | |
348 * | Authentication Token |
349 * |________________________________|
350 *
351 */
352
353/*
354 * Security Trailer:
355 *
356 * The sec_trailer structure MUST be placed at the end of the PDU, including past stub data,
357 * when present. The sec_trailer structure MUST be 4-byte aligned with respect to the beginning
358 * of the PDU. Padding octets MUST be used to align the sec_trailer structure if its natural
359 * beginning is not already 4-byte aligned.
360 *
361 * All PDUs that carry sec_trailer information share certain common fields:
362 * frag_length and auth_length. The beginning of the sec_trailer structure for each PDU MUST be
363 * calculated to start from offset (frag_length – auth_length – 8) from the beginning of the PDU.
364 *
365 * Immediately after the sec_trailer structure, there MUST be a BLOB carrying the authentication
366 * information produced by the security provider. This BLOB is called the authentication token and
367 * MUST be of size auth_length. The size MUST also be equal to the length from the first octet
368 * immediately after the sec_trailer structure all the way to the end of the fragment;
369 * the two values MUST be the same.
370 *
371 * A client or a server that (during composing of a PDU) has allocated more space for the
372 * authentication token than the security provider fills in SHOULD fill in the rest of
373 * the allocated space with zero octets. These zero octets are still considered to belong
374 * to the authentication token part of the PDU.
375 *
376 */
377
378BOOL rpc_get_stub_data_info(rdpRpc* rpc, const rpcconn_hdr_t* header, size_t* poffset,
379 size_t* length)
380{
381 size_t used = 0;
382 size_t offset = 0;
383 BOOL rc = FALSE;
384 UINT32 frag_length = 0;
385 UINT32 auth_length = 0;
386 UINT32 auth_pad_length = 0;
387 UINT32 sec_trailer_offset = 0;
388 const rpc_sec_trailer* sec_trailer = NULL;
389
390 WINPR_ASSERT(rpc);
391 WINPR_ASSERT(header);
392 WINPR_ASSERT(poffset);
393 WINPR_ASSERT(length);
394
395 offset = RPC_COMMON_FIELDS_LENGTH;
396
397 switch (header->common.ptype)
398 {
399 case PTYPE_RESPONSE:
400 offset += 8;
401 rpc_offset_align(&offset, 8);
402 sec_trailer = &header->response.auth_verifier;
403 break;
404
405 case PTYPE_REQUEST:
406 offset += 4;
407 rpc_offset_align(&offset, 8);
408 sec_trailer = &header->request.auth_verifier;
409 break;
410
411 case PTYPE_RTS:
412 offset += 4;
413 break;
414
415 default:
416 WLog_Print(rpc->log, WLOG_ERROR, "Unknown PTYPE: 0x%02" PRIX8 "", header->common.ptype);
417 goto fail;
418 }
419
420 frag_length = header->common.frag_length;
421 auth_length = header->common.auth_length;
422
423 if (poffset)
424 *poffset = offset;
425
426 /* The fragment must be larger than the authentication trailer */
427 used = offset + auth_length + 8ull;
428 if (sec_trailer)
429 {
430 auth_pad_length = sec_trailer->auth_pad_length;
431 used += sec_trailer->auth_pad_length;
432 }
433
434 if (frag_length < used)
435 goto fail;
436
437 if (!length)
438 return TRUE;
439
440 sec_trailer_offset = frag_length - auth_length - 8;
441
442 /*
443 * According to [MS-RPCE], auth_pad_length is the number of padding
444 * octets used to 4-byte align the security trailer, but in practice
445 * we get values up to 15, which indicates 16-byte alignment.
446 */
447
448 if ((frag_length - (sec_trailer_offset + 8)) != auth_length)
449 {
450 WLog_Print(rpc->log, WLOG_ERROR,
451 "invalid auth_length: actual: %" PRIu32 ", expected: %" PRIu32 "", auth_length,
452 (frag_length - (sec_trailer_offset + 8)));
453 }
454
455 *length = sec_trailer_offset - auth_pad_length - offset;
456
457 rc = TRUE;
458fail:
459 return rc;
460}
461
462SSIZE_T rpc_channel_read(RpcChannel* channel, wStream* s, size_t length)
463{
464 int status = 0;
465
466 if (!channel || (length > INT32_MAX))
467 return -1;
468
469 ERR_clear_error();
470 status = BIO_read(channel->tls->bio, Stream_Pointer(s), (INT32)length);
471
472 if (status > 0)
473 {
474 Stream_Seek(s, (size_t)status);
475 return status;
476 }
477
478 if (BIO_should_retry(channel->tls->bio))
479 return 0;
480
481 WLog_Print(channel->rpc->log, WLOG_ERROR, "rpc_channel_read: Out of retries");
482 return -1;
483}
484
485SSIZE_T rpc_channel_write_int(RpcChannel* channel, const BYTE* data, size_t length,
486 const char* file, size_t line, const char* fkt)
487{
488 WINPR_ASSERT(channel);
489 WINPR_ASSERT(channel->rpc);
490
491 const DWORD level = WLOG_TRACE;
492 if (WLog_IsLevelActive(channel->rpc->log, level))
493 {
494 WLog_PrintMessage(channel->rpc->log, WLOG_MESSAGE_TEXT, level, line, file, fkt,
495 "Sending [%s] %" PRIuz " bytes", fkt, length);
496 }
497
498 return freerdp_tls_write_all(channel->tls, data, length);
499}
500
501BOOL rpc_in_channel_transition_to_state(RpcInChannel* inChannel, CLIENT_IN_CHANNEL_STATE state)
502{
503 if (!inChannel)
504 return FALSE;
505
506 inChannel->State = state;
507 WLog_Print(inChannel->common.rpc->log, WLOG_DEBUG, "%s", client_in_state_str(state));
508 return TRUE;
509}
510
511static int rpc_channel_rpch_init(RpcClient* client, RpcChannel* channel, const char* inout,
512 const GUID* guid)
513{
514 HttpContext* http = NULL;
515 rdpSettings* settings = NULL;
516 UINT32 timeout = 0;
517
518 if (!client || !channel || !inout || !client->context || !client->context->settings)
519 return -1;
520
521 settings = client->context->settings;
522 channel->auth = credssp_auth_new(client->context);
523 rts_generate_cookie((BYTE*)&channel->Cookie);
524 channel->client = client;
525
526 if (!channel->auth)
527 return -1;
528
529 channel->http = http_context_new();
530
531 if (!channel->http)
532 return -1;
533
534 http = channel->http;
535
536 {
537 if (!http_context_set_pragma(http, "ResourceTypeUuid=44e265dd-7daf-42cd-8560-3cdb6e7a2729"))
538 return -1;
539
540 if (guid)
541 {
542 char* strguid = NULL;
543 RPC_STATUS rpcStatus = UuidToStringA(guid, &strguid);
544
545 if (rpcStatus != RPC_S_OK)
546 return -1;
547
548 const BOOL rc = http_context_append_pragma(http, "SessionId=%s", strguid);
549 RpcStringFreeA(&strguid);
550 if (!rc)
551 return -1;
552 }
553 if (timeout)
554 {
555 if (!http_context_append_pragma(http, "MinConnTimeout=%" PRIu32, timeout))
556 return -1;
557 }
558
559 if (!http_context_set_rdg_correlation_id(http, guid) ||
560 !http_context_set_rdg_connection_id(http, guid))
561 return -1;
562 }
563
564 /* TODO: "/rpcwithcert/rpcproxy.dll". */
565 if (!http_context_set_method(http, inout) ||
566 !http_context_set_uri(http, "/rpc/rpcproxy.dll?localhost:3388") ||
567 !http_context_set_accept(http, "application/rpc") ||
568 !http_context_set_cache_control(http, "no-cache") ||
569 !http_context_set_connection(http, "Keep-Alive") ||
570 !http_context_set_user_agent(http, "MSRPC") ||
571 !http_context_set_host(http, settings->GatewayHostname))
572 return -1;
573
574 return 1;
575}
576
577static int rpc_in_channel_init(rdpRpc* rpc, RpcInChannel* inChannel, const GUID* guid)
578{
579 WINPR_ASSERT(rpc);
580 WINPR_ASSERT(inChannel);
581
582 inChannel->common.rpc = rpc;
583 inChannel->State = CLIENT_IN_CHANNEL_STATE_INITIAL;
584 inChannel->BytesSent = 0;
585 inChannel->SenderAvailableWindow = rpc->ReceiveWindow;
586 inChannel->PingOriginator.ConnectionTimeout = 30;
587 inChannel->PingOriginator.KeepAliveInterval = 0;
588
589 if (rpc_channel_rpch_init(rpc->client, &inChannel->common, "RPC_IN_DATA", guid) < 0)
590 return -1;
591
592 return 1;
593}
594
595static RpcInChannel* rpc_in_channel_new(rdpRpc* rpc, const GUID* guid)
596{
597 RpcInChannel* inChannel = (RpcInChannel*)calloc(1, sizeof(RpcInChannel));
598
599 if (inChannel)
600 {
601 rpc_in_channel_init(rpc, inChannel, guid);
602 }
603
604 return inChannel;
605}
606
607void rpc_channel_free(RpcChannel* channel)
608{
609 if (!channel)
610 return;
611
612 credssp_auth_free(channel->auth);
613 http_context_free(channel->http);
614 freerdp_tls_free(channel->tls);
615 free(channel);
616}
617
618BOOL rpc_out_channel_transition_to_state(RpcOutChannel* outChannel, CLIENT_OUT_CHANNEL_STATE state)
619{
620 if (!outChannel)
621 return FALSE;
622
623 outChannel->State = state;
624 WLog_Print(outChannel->common.rpc->log, WLOG_DEBUG, "%s", client_out_state_str(state));
625 return TRUE;
626}
627
628static int rpc_out_channel_init(rdpRpc* rpc, RpcOutChannel* outChannel, const GUID* guid)
629{
630 WINPR_ASSERT(rpc);
631 WINPR_ASSERT(outChannel);
632
633 outChannel->common.rpc = rpc;
634 outChannel->State = CLIENT_OUT_CHANNEL_STATE_INITIAL;
635 outChannel->BytesReceived = 0;
636 outChannel->ReceiverAvailableWindow = rpc->ReceiveWindow;
637 outChannel->ReceiveWindow = rpc->ReceiveWindow;
638 outChannel->ReceiveWindowSize = rpc->ReceiveWindow;
639 outChannel->AvailableWindowAdvertised = rpc->ReceiveWindow;
640
641 if (rpc_channel_rpch_init(rpc->client, &outChannel->common, "RPC_OUT_DATA", guid) < 0)
642 return -1;
643
644 return 1;
645}
646
647RpcOutChannel* rpc_out_channel_new(rdpRpc* rpc, const GUID* guid)
648{
649 RpcOutChannel* outChannel = (RpcOutChannel*)calloc(1, sizeof(RpcOutChannel));
650
651 if (outChannel)
652 {
653 rpc_out_channel_init(rpc, outChannel, guid);
654 }
655
656 return outChannel;
657}
658
659BOOL rpc_virtual_connection_transition_to_state(rdpRpc* rpc, RpcVirtualConnection* connection,
660 VIRTUAL_CONNECTION_STATE state)
661{
662 if (!connection)
663 return FALSE;
664
665 WINPR_ASSERT(rpc);
666 connection->State = state;
667 WLog_Print(rpc->log, WLOG_DEBUG, "%s", rpc_vc_state_str(state));
668 return TRUE;
669}
670
671static void rpc_virtual_connection_free(RpcVirtualConnection* connection)
672{
673 if (!connection)
674 return;
675
676 if (connection->DefaultInChannel)
677 rpc_channel_free(&connection->DefaultInChannel->common);
678 if (connection->NonDefaultInChannel)
679 rpc_channel_free(&connection->NonDefaultInChannel->common);
680 if (connection->DefaultOutChannel)
681 rpc_channel_free(&connection->DefaultOutChannel->common);
682 if (connection->NonDefaultOutChannel)
683 rpc_channel_free(&connection->NonDefaultOutChannel->common);
684 free(connection);
685}
686
687static RpcVirtualConnection* rpc_virtual_connection_new(rdpRpc* rpc)
688{
689 WINPR_ASSERT(rpc);
690
691 RpcVirtualConnection* connection =
692 (RpcVirtualConnection*)calloc(1, sizeof(RpcVirtualConnection));
693
694 if (!connection)
695 return NULL;
696
697 rts_generate_cookie((BYTE*)&(connection->Cookie));
698 rts_generate_cookie((BYTE*)&(connection->AssociationGroupId));
699 connection->State = VIRTUAL_CONNECTION_STATE_INITIAL;
700
701 connection->DefaultInChannel = rpc_in_channel_new(rpc, &connection->Cookie);
702
703 if (!connection->DefaultInChannel)
704 goto fail;
705
706 connection->DefaultOutChannel = rpc_out_channel_new(rpc, &connection->Cookie);
707
708 if (!connection->DefaultOutChannel)
709 goto fail;
710
711 return connection;
712fail:
713 rpc_virtual_connection_free(connection);
714 return NULL;
715}
716
717static BOOL rpc_channel_tls_connect(RpcChannel* channel, UINT32 timeout)
718{
719 if (!channel || !channel->client || !channel->client->context ||
720 !channel->client->context->settings)
721 return FALSE;
722
723 rdpContext* context = channel->client->context;
724 WINPR_ASSERT(context);
725
726 rdpSettings* settings = context->settings;
727 WINPR_ASSERT(settings);
728
729 const char* proxyUsername = freerdp_settings_get_string(settings, FreeRDP_ProxyUsername);
730 const char* proxyPassword = freerdp_settings_get_string(settings, FreeRDP_ProxyPassword);
731
732 rdpTransport* transport = freerdp_get_transport(context);
733 rdpTransportLayer* layer =
734 transport_connect_layer(transport, channel->client->host, channel->client->port, timeout);
735
736 if (!layer)
737 return FALSE;
738
739 BIO* layerBio = BIO_new(BIO_s_transport_layer());
740 if (!layerBio)
741 {
742 transport_layer_free(layer);
743 return FALSE;
744 }
745 BIO_set_data(layerBio, layer);
746
747 BIO* bufferedBio = BIO_new(BIO_s_buffered_socket());
748 if (!bufferedBio)
749 {
750 BIO_free_all(layerBio);
751 return FALSE;
752 }
753
754 bufferedBio = BIO_push(bufferedBio, layerBio);
755
756 if (!BIO_set_nonblock(bufferedBio, TRUE))
757 {
758 BIO_free_all(bufferedBio);
759 return FALSE;
760 }
761
762 if (channel->client->isProxy)
763 {
764 WINPR_ASSERT(settings->GatewayPort <= UINT16_MAX);
765 if (!proxy_connect(context, bufferedBio, proxyUsername, proxyPassword,
766 settings->GatewayHostname, (UINT16)settings->GatewayPort))
767 {
768 BIO_free_all(bufferedBio);
769 return FALSE;
770 }
771 }
772
773 channel->bio = bufferedBio;
774 rdpTls* tls = channel->tls = freerdp_tls_new(context);
775
776 if (!tls)
777 return FALSE;
778
779 tls->hostname = settings->GatewayHostname;
780 tls->port = WINPR_ASSERTING_INT_CAST(int32_t, MIN(UINT16_MAX, settings->GatewayPort));
781 tls->isGatewayTransport = TRUE;
782 int tlsStatus = freerdp_tls_connect(tls, bufferedBio);
783
784 if (tlsStatus < 1)
785 {
786 if (tlsStatus < 0)
787 {
788 freerdp_set_last_error_if_not(context, FREERDP_ERROR_TLS_CONNECT_FAILED);
789 }
790 else
791 {
792 freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_CANCELLED);
793 }
794
795 return FALSE;
796 }
797
798 return TRUE;
799}
800
801static int rpc_in_channel_connect(RpcInChannel* inChannel, UINT32 timeout)
802{
803 rdpContext* context = NULL;
804
805 if (!inChannel || !inChannel->common.client || !inChannel->common.client->context)
806 return -1;
807
808 context = inChannel->common.client->context;
809
810 /* Connect IN Channel */
811
812 if (!rpc_channel_tls_connect(&inChannel->common, timeout))
813 return -1;
814
815 rpc_in_channel_transition_to_state(inChannel, CLIENT_IN_CHANNEL_STATE_CONNECTED);
816
817 if (!rpc_ncacn_http_auth_init(context, &inChannel->common))
818 return -1;
819
820 /* Send IN Channel Request */
821
822 if (!rpc_ncacn_http_send_in_channel_request(&inChannel->common))
823 {
824 WLog_Print(inChannel->common.rpc->log, WLOG_ERROR,
825 "rpc_ncacn_http_send_in_channel_request failure");
826 return -1;
827 }
828
829 if (!rpc_in_channel_transition_to_state(inChannel, CLIENT_IN_CHANNEL_STATE_SECURITY))
830 return -1;
831
832 return 1;
833}
834
835static int rpc_out_channel_connect(RpcOutChannel* outChannel, UINT32 timeout)
836{
837 rdpContext* context = NULL;
838
839 if (!outChannel || !outChannel->common.client || !outChannel->common.client->context)
840 return -1;
841
842 context = outChannel->common.client->context;
843
844 /* Connect OUT Channel */
845
846 if (!rpc_channel_tls_connect(&outChannel->common, timeout))
847 return -1;
848
849 rpc_out_channel_transition_to_state(outChannel, CLIENT_OUT_CHANNEL_STATE_CONNECTED);
850
851 if (!rpc_ncacn_http_auth_init(context, &outChannel->common))
852 return FALSE;
853
854 /* Send OUT Channel Request */
855
856 if (!rpc_ncacn_http_send_out_channel_request(&outChannel->common, FALSE))
857 {
858 WLog_Print(outChannel->common.rpc->log, WLOG_ERROR,
859 "rpc_ncacn_http_send_out_channel_request failure");
860 return FALSE;
861 }
862
863 rpc_out_channel_transition_to_state(outChannel, CLIENT_OUT_CHANNEL_STATE_SECURITY);
864 return 1;
865}
866
867int rpc_out_channel_replacement_connect(RpcOutChannel* outChannel, uint32_t timeout)
868{
869 rdpContext* context = NULL;
870
871 if (!outChannel || !outChannel->common.client || !outChannel->common.client->context)
872 return -1;
873
874 context = outChannel->common.client->context;
875
876 /* Connect OUT Channel */
877
878 if (!rpc_channel_tls_connect(&outChannel->common, timeout))
879 return -1;
880
881 rpc_out_channel_transition_to_state(outChannel, CLIENT_OUT_CHANNEL_STATE_CONNECTED);
882
883 if (!rpc_ncacn_http_auth_init(context, (RpcChannel*)outChannel))
884 return FALSE;
885
886 /* Send OUT Channel Request */
887
888 if (!rpc_ncacn_http_send_out_channel_request(&outChannel->common, TRUE))
889 {
890 WLog_Print(outChannel->common.rpc->log, WLOG_ERROR,
891 "rpc_ncacn_http_send_out_channel_request failure");
892 return FALSE;
893 }
894
895 rpc_out_channel_transition_to_state(outChannel, CLIENT_OUT_CHANNEL_STATE_SECURITY);
896 return 1;
897}
898
899BOOL rpc_connect(rdpRpc* rpc, UINT32 timeout)
900{
901 RpcInChannel* inChannel = NULL;
902 RpcOutChannel* outChannel = NULL;
903 RpcVirtualConnection* connection = NULL;
904 rpc->VirtualConnection = rpc_virtual_connection_new(rpc);
905
906 if (!rpc->VirtualConnection)
907 return FALSE;
908
909 connection = rpc->VirtualConnection;
910 inChannel = connection->DefaultInChannel;
911 outChannel = connection->DefaultOutChannel;
912 rpc_virtual_connection_transition_to_state(rpc, connection, VIRTUAL_CONNECTION_STATE_INITIAL);
913
914 if (rpc_in_channel_connect(inChannel, timeout) < 0)
915 return FALSE;
916
917 if (rpc_out_channel_connect(outChannel, timeout) < 0)
918 return FALSE;
919
920 return TRUE;
921}
922
923rdpRpc* rpc_new(rdpTransport* transport)
924{
925 rdpContext* context = transport_get_context(transport);
926 rdpRpc* rpc = NULL;
927
928 WINPR_ASSERT(context);
929
930 rpc = (rdpRpc*)calloc(1, sizeof(rdpRpc));
931
932 if (!rpc)
933 return NULL;
934
935 rpc->log = WLog_Get(TAG);
936 rpc->State = RPC_CLIENT_STATE_INITIAL;
937 rpc->transport = transport;
938 rpc->SendSeqNum = 0;
939 rpc->auth = credssp_auth_new(context);
940
941 if (!rpc->auth)
942 goto out_free;
943
944 rpc->PipeCallId = 0;
945 rpc->StubCallId = 0;
946 rpc->StubFragCount = 0;
947 rpc->rpc_vers = 5;
948 rpc->rpc_vers_minor = 0;
949 /* little-endian data representation */
950 rpc->packed_drep[0] = 0x10;
951 rpc->packed_drep[1] = 0x00;
952 rpc->packed_drep[2] = 0x00;
953 rpc->packed_drep[3] = 0x00;
954 rpc->max_xmit_frag = 0x0FF8;
955 rpc->max_recv_frag = 0x0FF8;
956 rpc->ReceiveWindow = 0x00010000;
957 rpc->ChannelLifetime = 0x40000000;
958 rpc->KeepAliveInterval = 300000;
959 rpc->CurrentKeepAliveInterval = rpc->KeepAliveInterval;
960 rpc->CurrentKeepAliveTime = 0;
961 rpc->CallId = 2;
962 rpc->client = rpc_client_new(context, rpc->max_recv_frag);
963
964 if (!rpc->client)
965 goto out_free;
966
967 return rpc;
968out_free:
969 WINPR_PRAGMA_DIAG_PUSH
970 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
971 rpc_free(rpc);
972 WINPR_PRAGMA_DIAG_POP
973 return NULL;
974}
975
976void rpc_free(rdpRpc* rpc)
977{
978 if (rpc)
979 {
980 rpc_client_free(rpc->client);
981 credssp_auth_free(rpc->auth);
982 rpc_virtual_connection_free(rpc->VirtualConnection);
983 free(rpc);
984 }
985}
FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.