FreeRDP
Loading...
Searching...
No Matches
server/remdesk_main.c
1
22#include <freerdp/config.h>
23
24#include <winpr/crt.h>
25#include <winpr/print.h>
26#include <winpr/stream.h>
27
28#include <freerdp/freerdp.h>
29
30#include "remdesk_main.h"
31#include "remdesk_common.h"
32
38static UINT remdesk_virtual_channel_write(RemdeskServerContext* context, wStream* s)
39{
40 const size_t len = Stream_Length(s);
41 WINPR_ASSERT(len <= UINT32_MAX);
42 ULONG BytesWritten = 0;
43 BOOL status = WTSVirtualChannelWrite(context->priv->ChannelHandle, Stream_BufferAs(s, char),
44 (UINT32)len, &BytesWritten);
45 return (status) ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
46}
47
53static UINT remdesk_send_ctl_result_pdu(RemdeskServerContext* context, UINT32 result)
54{
55 wStream* s = nullptr;
57 UINT error = 0;
58 pdu.result = result;
59
60 if ((error = remdesk_prepare_ctl_header(&(pdu.ctlHeader), REMDESK_CTL_RESULT, 4)))
61 {
62 WLog_ERR(TAG, "remdesk_prepare_ctl_header failed with error %" PRIu32 "!", error);
63 return error;
64 }
65
66 s = Stream_New(nullptr, REMDESK_CHANNEL_CTL_SIZE + pdu.ctlHeader.ch.DataLength);
67
68 if (!s)
69 {
70 WLog_ERR(TAG, "Stream_New failed!");
71 return CHANNEL_RC_NO_MEMORY;
72 }
73
74 if ((error = remdesk_write_ctl_header(s, &(pdu.ctlHeader))))
75 {
76 WLog_ERR(TAG, "remdesk_write_ctl_header failed with error %" PRIu32 "!", error);
77 goto out;
78 }
79
80 Stream_Write_UINT32(s, pdu.result); /* result (4 bytes) */
81 Stream_SealLength(s);
82
83 if ((error = remdesk_virtual_channel_write(context, s)))
84 WLog_ERR(TAG, "remdesk_virtual_channel_write failed with error %" PRIu32 "!", error);
85
86out:
87 Stream_Free(s, TRUE);
88 return error;
89}
90
96static UINT remdesk_send_ctl_version_info_pdu(RemdeskServerContext* context)
97{
98 wStream* s = nullptr;
100 UINT error = 0;
101
102 if ((error = remdesk_prepare_ctl_header(&(pdu.ctlHeader), REMDESK_CTL_VERSIONINFO, 8)))
103 {
104 WLog_ERR(TAG, "remdesk_prepare_ctl_header failed with error %" PRIu32 "!", error);
105 return error;
106 }
107
108 pdu.versionMajor = 1;
109 pdu.versionMinor = 2;
110 s = Stream_New(nullptr, REMDESK_CHANNEL_CTL_SIZE + pdu.ctlHeader.ch.DataLength);
111
112 if (!s)
113 {
114 WLog_ERR(TAG, "Stream_New failed!");
115 return CHANNEL_RC_NO_MEMORY;
116 }
117
118 if ((error = remdesk_write_ctl_header(s, &(pdu.ctlHeader))))
119 {
120 WLog_ERR(TAG, "remdesk_write_ctl_header failed with error %" PRIu32 "!", error);
121 goto out;
122 }
123
124 Stream_Write_UINT32(s, pdu.versionMajor); /* versionMajor (4 bytes) */
125 Stream_Write_UINT32(s, pdu.versionMinor); /* versionMinor (4 bytes) */
126 Stream_SealLength(s);
127
128 if ((error = remdesk_virtual_channel_write(context, s)))
129 WLog_ERR(TAG, "remdesk_virtual_channel_write failed with error %" PRIu32 "!", error);
130
131out:
132 Stream_Free(s, TRUE);
133 return error;
134}
135
141static UINT remdesk_recv_ctl_version_info_pdu(WINPR_ATTR_UNUSED RemdeskServerContext* context,
142 wStream* s,
143 WINPR_ATTR_UNUSED REMDESK_CHANNEL_HEADER* header)
144{
145 UINT32 versionMajor = 0;
146 UINT32 versionMinor = 0;
147
148 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
149 return ERROR_INVALID_DATA;
150
151 Stream_Read_UINT32(s, versionMajor); /* versionMajor (4 bytes) */
152 Stream_Read_UINT32(s, versionMinor); /* versionMinor (4 bytes) */
153 if ((versionMajor != 1) || (versionMinor != 2))
154 {
155 WLog_ERR(TAG, "REMOTEDESKTOP_CTL_VERSIONINFO_PACKET invalid version %" PRIu32 ".%" PRIu32,
156 versionMajor, versionMinor);
157 return ERROR_INVALID_DATA;
158 }
159
160 return CHANNEL_RC_OK;
161}
162
168static UINT remdesk_recv_ctl_remote_control_desktop_pdu(RemdeskServerContext* context, wStream* s,
170{
171 size_t cchStringW = 0;
172 REMDESK_CTL_REMOTE_CONTROL_DESKTOP_PDU pdu = WINPR_C_ARRAY_INIT;
173 UINT error = 0;
174
175 size_t cchMax = header->DataLength - 4;
176 const size_t remaining = Stream_GetRemainingLength(s);
177 if (cchMax > remaining)
178 cchMax = remaining;
179 cchMax /= sizeof(WCHAR);
180
181 const WCHAR* pStringW = Stream_ConstPointer(s);
182 const WCHAR* raConnectionStringW = pStringW;
183
184 while ((cchStringW < cchMax) && pStringW[cchStringW])
185 cchStringW++;
186
187 if ((cchStringW >= cchMax) || !cchStringW)
188 return ERROR_INVALID_DATA;
189
190 cchStringW++;
191 const size_t cbRaConnectionStringW = cchStringW * sizeof(WCHAR);
192 pdu.raConnectionString = ConvertWCharNToUtf8Alloc(
193 raConnectionStringW, cbRaConnectionStringW / sizeof(WCHAR), nullptr);
194 if (!pdu.raConnectionString)
195 return ERROR_INTERNAL_ERROR;
196
197 WLog_INFO(TAG, "RaConnectionString: %s", pdu.raConnectionString);
198 free(pdu.raConnectionString);
199
200 if ((error = remdesk_send_ctl_result_pdu(context, 0)))
201 WLog_ERR(TAG, "remdesk_send_ctl_result_pdu failed with error %" PRIu32 "!", error);
202
203 return error;
204}
205
211static UINT remdesk_recv_ctl_authenticate_pdu(WINPR_ATTR_UNUSED RemdeskServerContext* context,
213{
214 size_t cchTmpStringW = 0;
215 const WCHAR* expertBlobW = nullptr;
216 REMDESK_CTL_AUTHENTICATE_PDU pdu = WINPR_C_ARRAY_INIT;
217
218 size_t cchRemaining = header->DataLength - 4;
219 const size_t remaining = Stream_GetRemainingLength(s);
220 if (cchRemaining > remaining)
221 cchRemaining = remaining;
222 cchRemaining /= sizeof(WCHAR);
223
224 const WCHAR* pStringW = Stream_ConstPointer(s);
225 const WCHAR* raConnectionStringW = pStringW;
226
227 while ((cchTmpStringW < cchRemaining) && pStringW[cchTmpStringW])
228 cchTmpStringW++;
229
230 if ((cchTmpStringW >= cchRemaining) || !cchTmpStringW)
231 return ERROR_INVALID_DATA;
232
233 cchTmpStringW++;
234 const size_t cbRaConnectionStringW = cchTmpStringW * sizeof(WCHAR);
235 pStringW += cchTmpStringW;
236 expertBlobW = pStringW;
237 cchRemaining -= cchTmpStringW;
238
239 size_t cchStringW = 0;
240 while ((cchStringW < cchRemaining) && pStringW[cchStringW])
241 cchStringW++;
242
243 if ((cchStringW >= cchRemaining) || !cchStringW)
244 return ERROR_INVALID_DATA;
245
246 cchStringW++;
247 const size_t cbExpertBlobW = cchStringW * sizeof(WCHAR);
248 pdu.raConnectionString = ConvertWCharNToUtf8Alloc(
249 raConnectionStringW, cbRaConnectionStringW / sizeof(WCHAR), nullptr);
250 if (!pdu.raConnectionString)
251 return ERROR_INTERNAL_ERROR;
252
253 pdu.expertBlob = ConvertWCharNToUtf8Alloc(expertBlobW, cbExpertBlobW / sizeof(WCHAR), nullptr);
254 if (!pdu.expertBlob)
255 {
256 free(pdu.raConnectionString);
257 return ERROR_INTERNAL_ERROR;
258 }
259
260 WLog_INFO(TAG, "RaConnectionString: %s ExpertBlob: %s", pdu.raConnectionString, pdu.expertBlob);
261 free(pdu.raConnectionString);
262 free(pdu.expertBlob);
263 return CHANNEL_RC_OK;
264}
265
271static UINT remdesk_recv_ctl_verify_password_pdu(RemdeskServerContext* context, wStream* s,
273{
274 REMDESK_CTL_VERIFY_PASSWORD_PDU pdu = WINPR_C_ARRAY_INIT;
275
276 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
277 return ERROR_INVALID_DATA;
278
279 const WCHAR* expertBlobW = Stream_ConstPointer(s);
280
281 size_t cbExpertBlobW = header->DataLength - 4;
282 const size_t remaining = Stream_GetRemainingLength(s);
283 if (cbExpertBlobW > remaining)
284 cbExpertBlobW = remaining;
285
286 pdu.expertBlob = ConvertWCharNToUtf8Alloc(expertBlobW, cbExpertBlobW / sizeof(WCHAR), nullptr);
287 if (!pdu.expertBlob)
288 return ERROR_INTERNAL_ERROR;
289
290 WLog_INFO(TAG, "ExpertBlob: %s", pdu.expertBlob);
291
292 // TODO: Callback?
293
294 free(pdu.expertBlob);
295 return remdesk_send_ctl_result_pdu(context, 0);
296}
297
303static UINT remdesk_recv_ctl_pdu(RemdeskServerContext* context, wStream* s,
305{
306 UINT error = CHANNEL_RC_OK;
307 UINT32 msgType = 0;
308
309 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
310 return ERROR_INVALID_DATA;
311
312 if (header->DataLength < 4)
313 return ERROR_INVALID_DATA;
314
315 Stream_Read_UINT32(s, msgType); /* msgType (4 bytes) */
316 WLog_INFO(TAG, "msgType: %" PRIu32 "", msgType);
317
318 switch (msgType)
319 {
320 case REMDESK_CTL_REMOTE_CONTROL_DESKTOP:
321 if ((error = remdesk_recv_ctl_remote_control_desktop_pdu(context, s, header)))
322 {
323 WLog_ERR(TAG,
324 "remdesk_recv_ctl_remote_control_desktop_pdu failed with error %" PRIu32
325 "!",
326 error);
327 return error;
328 }
329
330 break;
331
332 case REMDESK_CTL_AUTHENTICATE:
333 if ((error = remdesk_recv_ctl_authenticate_pdu(context, s, header)))
334 {
335 WLog_ERR(TAG, "remdesk_recv_ctl_authenticate_pdu failed with error %" PRIu32 "!",
336 error);
337 return error;
338 }
339
340 break;
341
342 case REMDESK_CTL_DISCONNECT:
343 break;
344
345 case REMDESK_CTL_VERSIONINFO:
346 if ((error = remdesk_recv_ctl_version_info_pdu(context, s, header)))
347 {
348 WLog_ERR(TAG, "remdesk_recv_ctl_version_info_pdu failed with error %" PRIu32 "!",
349 error);
350 return error;
351 }
352
353 break;
354
355 case REMDESK_CTL_ISCONNECTED:
356 break;
357
358 case REMDESK_CTL_VERIFY_PASSWORD:
359 if ((error = remdesk_recv_ctl_verify_password_pdu(context, s, header)))
360 {
361 WLog_ERR(TAG, "remdesk_recv_ctl_verify_password_pdu failed with error %" PRIu32 "!",
362 error);
363 return error;
364 }
365
366 break;
367
368 case REMDESK_CTL_EXPERT_ON_VISTA:
369 break;
370
371 case REMDESK_CTL_RANOVICE_NAME:
372 break;
373
374 case REMDESK_CTL_RAEXPERT_NAME:
375 break;
376
377 case REMDESK_CTL_TOKEN:
378 break;
379
380 default:
381 WLog_ERR(TAG, "remdesk_recv_control_pdu: unknown msgType: %" PRIu32 "", msgType);
382 error = ERROR_INVALID_DATA;
383 break;
384 }
385
386 return error;
387}
388
394static UINT remdesk_server_receive_pdu(RemdeskServerContext* context, wStream* s)
395{
396 UINT error = CHANNEL_RC_OK;
398
399 if ((error = remdesk_read_channel_header(s, &header)))
400 {
401 WLog_ERR(TAG, "remdesk_read_channel_header failed with error %" PRIu32 "!", error);
402 return error;
403 }
404
405 if (strcmp(header.ChannelName, "RC_CTL") == 0)
406 {
407 if ((error = remdesk_recv_ctl_pdu(context, s, &header)))
408 {
409 WLog_ERR(TAG, "remdesk_recv_ctl_pdu failed with error %" PRIu32 "!", error);
410 return error;
411 }
412 }
413 else if (strcmp(header.ChannelName, "70") == 0)
414 {
415 }
416 else if (strcmp(header.ChannelName, "71") == 0)
417 {
418 }
419 else if (strcmp(header.ChannelName, ".") == 0)
420 {
421 }
422 else if (strcmp(header.ChannelName, "1000.") == 0)
423 {
424 }
425 else if (strcmp(header.ChannelName, "RA_FX") == 0)
426 {
427 }
428 else
429 {
430 }
431
432 return error;
433}
434
435static DWORD WINAPI remdesk_server_thread(LPVOID arg)
436{
437 void* buffer = nullptr;
438 HANDLE events[8] = WINPR_C_ARRAY_INIT;
439 HANDLE ChannelEvent = nullptr;
440 DWORD BytesReturned = 0;
441 UINT error = 0;
442 RemdeskServerContext* context = (RemdeskServerContext*)arg;
443 WINPR_ASSERT(context);
444 wStream* s = Stream_New(nullptr, 4096);
445
446 if (!s)
447 {
448 WLog_ERR(TAG, "Stream_New failed!");
449 error = CHANNEL_RC_NO_MEMORY;
450 goto out;
451 }
452
453 if (WTSVirtualChannelQuery(context->priv->ChannelHandle, WTSVirtualEventHandle, &buffer,
454 &BytesReturned) == TRUE)
455 {
456 if (BytesReturned == sizeof(HANDLE))
457 ChannelEvent = *(HANDLE*)buffer;
458
459 WTSFreeMemory(buffer);
460 }
461 else
462 {
463 WLog_ERR(TAG, "WTSVirtualChannelQuery failed!");
464 error = ERROR_INTERNAL_ERROR;
465 goto out;
466 }
467
468 {
469 DWORD nCount = 0;
470 events[nCount++] = ChannelEvent;
471 events[nCount++] = context->priv->StopEvent;
472
473 if ((error = remdesk_send_ctl_version_info_pdu(context)))
474 {
475 WLog_ERR(TAG, "remdesk_send_ctl_version_info_pdu failed with error %" PRIu32 "!",
476 error);
477 goto out;
478 }
479
480 while (1)
481 {
482 DWORD status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
483
484 if (status == WAIT_FAILED)
485 {
486 error = GetLastError();
487 WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
488 break;
489 }
490
491 status = WaitForSingleObject(context->priv->StopEvent, 0);
492
493 if (status == WAIT_FAILED)
494 {
495 error = GetLastError();
496 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
497 break;
498 }
499
500 if (status == WAIT_OBJECT_0)
501 {
502 break;
503 }
504
505 const size_t len = Stream_Capacity(s);
506 if (len > UINT32_MAX)
507 {
508 error = ERROR_INTERNAL_ERROR;
509 break;
510 }
511 if (WTSVirtualChannelRead(context->priv->ChannelHandle, 0, Stream_BufferAs(s, char),
512 (UINT32)len, &BytesReturned))
513 {
514 if (BytesReturned)
515 Stream_Seek(s, BytesReturned);
516 }
517 else
518 {
519 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
520 {
521 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
522 error = CHANNEL_RC_NO_MEMORY;
523 break;
524 }
525 }
526
527 if (Stream_GetPosition(s) >= 8)
528 {
529 const UINT32* pHeader = Stream_BufferAs(s, UINT32);
530 const UINT32 PduLength = pHeader[0] + pHeader[1] + 8;
531
532 if (PduLength >= Stream_GetPosition(s))
533 {
534 Stream_SealLength(s);
535 Stream_ResetPosition(s);
536
537 error = remdesk_server_receive_pdu(context, s);
538 if (error)
539 {
540 WLog_ERR(TAG, "remdesk_server_receive_pdu failed with error %" PRIu32 "!",
541 error);
542 break;
543 }
544
545 Stream_ResetPosition(s);
546 }
547 }
548 }
549 }
550out:
551 Stream_Free(s, TRUE);
552
553 if (error && context->rdpcontext)
554 setChannelError(context->rdpcontext, error, "remdesk_server_thread reported an error");
555
556 ExitThread(error);
557 return error;
558}
559
565static UINT remdesk_server_start(RemdeskServerContext* context)
566{
567 context->priv->ChannelHandle =
568 WTSVirtualChannelOpen(context->vcm, WTS_CURRENT_SESSION, REMDESK_SVC_CHANNEL_NAME);
569
570 if (!context->priv->ChannelHandle)
571 {
572 WLog_ERR(TAG, "WTSVirtualChannelOpen failed!");
573 return ERROR_INTERNAL_ERROR;
574 }
575
576 if (!(context->priv->StopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
577 {
578 WLog_ERR(TAG, "CreateEvent failed!");
579 return ERROR_INTERNAL_ERROR;
580 }
581
582 if (!(context->priv->Thread =
583 CreateThread(nullptr, 0, remdesk_server_thread, (void*)context, 0, nullptr)))
584 {
585 WLog_ERR(TAG, "CreateThread failed!");
586 (void)CloseHandle(context->priv->StopEvent);
587 context->priv->StopEvent = nullptr;
588 return ERROR_INTERNAL_ERROR;
589 }
590
591 return CHANNEL_RC_OK;
592}
593
599static UINT remdesk_server_stop(RemdeskServerContext* context)
600{
601 UINT error = 0;
602 (void)SetEvent(context->priv->StopEvent);
603
604 if (WaitForSingleObject(context->priv->Thread, INFINITE) == WAIT_FAILED)
605 {
606 error = GetLastError();
607 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error);
608 return error;
609 }
610
611 (void)CloseHandle(context->priv->Thread);
612 (void)CloseHandle(context->priv->StopEvent);
613 return CHANNEL_RC_OK;
614}
615
616RemdeskServerContext* remdesk_server_context_new(HANDLE vcm)
617{
618 RemdeskServerContext* context = nullptr;
619 context = (RemdeskServerContext*)calloc(1, sizeof(RemdeskServerContext));
620
621 if (context)
622 {
623 context->vcm = vcm;
624 context->Start = remdesk_server_start;
625 context->Stop = remdesk_server_stop;
626 context->priv = (RemdeskServerPrivate*)calloc(1, sizeof(RemdeskServerPrivate));
627
628 if (!context->priv)
629 {
630 free(context);
631 return nullptr;
632 }
633
634 context->priv->Version = 1;
635 }
636
637 return context;
638}
639
640void remdesk_server_context_free(RemdeskServerContext* context)
641{
642 if (context)
643 {
644 if (context->priv->ChannelHandle != INVALID_HANDLE_VALUE)
645 (void)WTSVirtualChannelClose(context->priv->ChannelHandle);
646
647 free(context->priv);
648 free(context);
649 }
650}