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