FreeRDP
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 
38 static 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 
53 static UINT remdesk_send_ctl_result_pdu(RemdeskServerContext* context, UINT32 result)
54 {
55  wStream* s = NULL;
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(NULL, 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 
86 out:
87  Stream_Free(s, TRUE);
88  return error;
89 }
90 
96 static UINT remdesk_send_ctl_version_info_pdu(RemdeskServerContext* context)
97 {
98  wStream* s = NULL;
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(NULL, 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 
131 out:
132  Stream_Free(s, TRUE);
133  return error;
134 }
135 
141 static UINT remdesk_recv_ctl_version_info_pdu(RemdeskServerContext* context, wStream* s,
142  REMDESK_CHANNEL_HEADER* header)
143 {
144  UINT32 versionMajor = 0;
145  UINT32 versionMinor = 0;
146 
147  if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
148  return ERROR_INVALID_DATA;
149 
150  Stream_Read_UINT32(s, versionMajor); /* versionMajor (4 bytes) */
151  Stream_Read_UINT32(s, versionMinor); /* versionMinor (4 bytes) */
152  if ((versionMajor != 1) || (versionMinor != 2))
153  {
154  WLog_ERR(TAG, "REMOTEDESKTOP_CTL_VERSIONINFO_PACKET invalid version %" PRIu32 ".%" PRIu32,
155  versionMajor, versionMinor);
156  return ERROR_INVALID_DATA;
157  }
158 
159  return CHANNEL_RC_OK;
160 }
161 
167 static UINT remdesk_recv_ctl_remote_control_desktop_pdu(RemdeskServerContext* context, wStream* s,
168  REMDESK_CHANNEL_HEADER* header)
169 {
170  size_t cchStringW = 0;
172  UINT error = 0;
173  UINT32 msgLength = header->DataLength - 4;
174  const WCHAR* pStringW = Stream_ConstPointer(s);
175  const WCHAR* raConnectionStringW = pStringW;
176 
177  while ((msgLength > 0) && pStringW[cchStringW])
178  {
179  msgLength -= 2;
180  cchStringW++;
181  }
182 
183  if (pStringW[cchStringW] || !cchStringW)
184  return ERROR_INVALID_DATA;
185 
186  cchStringW++;
187  const size_t cbRaConnectionStringW = cchStringW * 2;
188  pdu.raConnectionString =
189  ConvertWCharNToUtf8Alloc(raConnectionStringW, cbRaConnectionStringW / sizeof(WCHAR), NULL);
190  if (!pdu.raConnectionString)
191  return ERROR_INTERNAL_ERROR;
192 
193  WLog_INFO(TAG, "RaConnectionString: %s", pdu.raConnectionString);
194  free(pdu.raConnectionString);
195 
196  if ((error = remdesk_send_ctl_result_pdu(context, 0)))
197  WLog_ERR(TAG, "remdesk_send_ctl_result_pdu failed with error %" PRIu32 "!", error);
198 
199  return error;
200 }
201 
207 static UINT remdesk_recv_ctl_authenticate_pdu(RemdeskServerContext* context, wStream* s,
208  REMDESK_CHANNEL_HEADER* header)
209 {
210  size_t cchTmpStringW = 0;
211  const WCHAR* expertBlobW = NULL;
212  REMDESK_CTL_AUTHENTICATE_PDU pdu = { 0 };
213  UINT32 msgLength = header->DataLength - 4;
214  const WCHAR* pStringW = Stream_ConstPointer(s);
215  const WCHAR* raConnectionStringW = pStringW;
216 
217  while ((msgLength > 0) && pStringW[cchTmpStringW])
218  {
219  msgLength -= 2;
220  cchTmpStringW++;
221  }
222 
223  if (pStringW[cchTmpStringW] || !cchTmpStringW)
224  return ERROR_INVALID_DATA;
225 
226  cchTmpStringW++;
227  const size_t cbRaConnectionStringW = cchTmpStringW * sizeof(WCHAR);
228  pStringW += cchTmpStringW;
229  expertBlobW = pStringW;
230 
231  size_t cchStringW = 0;
232  while ((msgLength > 0) && pStringW[cchStringW])
233  {
234  msgLength -= 2;
235  cchStringW++;
236  }
237 
238  if (pStringW[cchStringW] || !cchStringW)
239  return ERROR_INVALID_DATA;
240 
241  cchStringW++;
242  const size_t cbExpertBlobW = cchStringW * 2;
243  pdu.raConnectionString =
244  ConvertWCharNToUtf8Alloc(raConnectionStringW, cbRaConnectionStringW / sizeof(WCHAR), NULL);
245  if (!pdu.raConnectionString)
246  return ERROR_INTERNAL_ERROR;
247 
248  pdu.expertBlob = ConvertWCharNToUtf8Alloc(expertBlobW, cbExpertBlobW / sizeof(WCHAR), NULL);
249  if (!pdu.expertBlob)
250  {
251  free(pdu.raConnectionString);
252  return ERROR_INTERNAL_ERROR;
253  }
254 
255  WLog_INFO(TAG, "RaConnectionString: %s ExpertBlob: %s", pdu.raConnectionString, pdu.expertBlob);
256  free(pdu.raConnectionString);
257  free(pdu.expertBlob);
258  return CHANNEL_RC_OK;
259 }
260 
266 static UINT remdesk_recv_ctl_verify_password_pdu(RemdeskServerContext* context, wStream* s,
267  REMDESK_CHANNEL_HEADER* header)
268 {
270  UINT error = 0;
271 
272  if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
273  return ERROR_INVALID_DATA;
274 
275  const WCHAR* expertBlobW = Stream_ConstPointer(s);
276  if (header->DataLength < 4)
277  return ERROR_INVALID_PARAMETER;
278 
279  const size_t cbExpertBlobW = header->DataLength - 4;
280 
281  pdu.expertBlob = ConvertWCharNToUtf8Alloc(expertBlobW, cbExpertBlobW / sizeof(WCHAR), NULL);
282  if (pdu.expertBlob)
283  return ERROR_INTERNAL_ERROR;
284 
285  WLog_INFO(TAG, "ExpertBlob: %s", pdu.expertBlob);
286 
287  if ((error = remdesk_send_ctl_result_pdu(context, 0)))
288  WLog_ERR(TAG, "remdesk_send_ctl_result_pdu failed with error %" PRIu32 "!", error);
289 
290  return error;
291 }
292 
298 static UINT remdesk_recv_ctl_pdu(RemdeskServerContext* context, wStream* s,
299  REMDESK_CHANNEL_HEADER* header)
300 {
301  UINT error = CHANNEL_RC_OK;
302  UINT32 msgType = 0;
303 
304  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
305  return ERROR_INVALID_DATA;
306 
307  Stream_Read_UINT32(s, msgType); /* msgType (4 bytes) */
308  WLog_INFO(TAG, "msgType: %" PRIu32 "", msgType);
309 
310  switch (msgType)
311  {
312  case REMDESK_CTL_REMOTE_CONTROL_DESKTOP:
313  if ((error = remdesk_recv_ctl_remote_control_desktop_pdu(context, s, header)))
314  {
315  WLog_ERR(TAG,
316  "remdesk_recv_ctl_remote_control_desktop_pdu failed with error %" PRIu32
317  "!",
318  error);
319  return error;
320  }
321 
322  break;
323 
324  case REMDESK_CTL_AUTHENTICATE:
325  if ((error = remdesk_recv_ctl_authenticate_pdu(context, s, header)))
326  {
327  WLog_ERR(TAG, "remdesk_recv_ctl_authenticate_pdu failed with error %" PRIu32 "!",
328  error);
329  return error;
330  }
331 
332  break;
333 
334  case REMDESK_CTL_DISCONNECT:
335  break;
336 
337  case REMDESK_CTL_VERSIONINFO:
338  if ((error = remdesk_recv_ctl_version_info_pdu(context, s, header)))
339  {
340  WLog_ERR(TAG, "remdesk_recv_ctl_version_info_pdu failed with error %" PRIu32 "!",
341  error);
342  return error;
343  }
344 
345  break;
346 
347  case REMDESK_CTL_ISCONNECTED:
348  break;
349 
350  case REMDESK_CTL_VERIFY_PASSWORD:
351  if ((error = remdesk_recv_ctl_verify_password_pdu(context, s, header)))
352  {
353  WLog_ERR(TAG, "remdesk_recv_ctl_verify_password_pdu failed with error %" PRIu32 "!",
354  error);
355  return error;
356  }
357 
358  break;
359 
360  case REMDESK_CTL_EXPERT_ON_VISTA:
361  break;
362 
363  case REMDESK_CTL_RANOVICE_NAME:
364  break;
365 
366  case REMDESK_CTL_RAEXPERT_NAME:
367  break;
368 
369  case REMDESK_CTL_TOKEN:
370  break;
371 
372  default:
373  WLog_ERR(TAG, "remdesk_recv_control_pdu: unknown msgType: %" PRIu32 "", msgType);
374  error = ERROR_INVALID_DATA;
375  break;
376  }
377 
378  return error;
379 }
380 
386 static UINT remdesk_server_receive_pdu(RemdeskServerContext* context, wStream* s)
387 {
388  UINT error = CHANNEL_RC_OK;
389  REMDESK_CHANNEL_HEADER header;
390 #if 0
391  WLog_INFO(TAG, "RemdeskReceive: %"PRIuz"", Stream_GetRemainingLength(s));
392  winpr_HexDump(WCHAR* expertBlobW = NULL;(s), Stream_GetRemainingLength(s));
393 #endif
394 
395  if ((error = remdesk_read_channel_header(s, &header)))
396  {
397  WLog_ERR(TAG, "remdesk_read_channel_header failed with error %" PRIu32 "!", error);
398  return error;
399  }
400 
401  if (strcmp(header.ChannelName, "RC_CTL") == 0)
402  {
403  if ((error = remdesk_recv_ctl_pdu(context, s, &header)))
404  {
405  WLog_ERR(TAG, "remdesk_recv_ctl_pdu failed with error %" PRIu32 "!", error);
406  return error;
407  }
408  }
409  else if (strcmp(header.ChannelName, "70") == 0)
410  {
411  }
412  else if (strcmp(header.ChannelName, "71") == 0)
413  {
414  }
415  else if (strcmp(header.ChannelName, ".") == 0)
416  {
417  }
418  else if (strcmp(header.ChannelName, "1000.") == 0)
419  {
420  }
421  else if (strcmp(header.ChannelName, "RA_FX") == 0)
422  {
423  }
424  else
425  {
426  }
427 
428  return error;
429 }
430 
431 static DWORD WINAPI remdesk_server_thread(LPVOID arg)
432 {
433  wStream* s = NULL;
434  DWORD status = 0;
435  DWORD nCount = 0;
436  void* buffer = NULL;
437  UINT32* pHeader = NULL;
438  UINT32 PduLength = 0;
439  HANDLE events[8];
440  HANDLE ChannelEvent = NULL;
441  DWORD BytesReturned = 0;
442  RemdeskServerContext* context = NULL;
443  UINT error = 0;
444  context = (RemdeskServerContext*)arg;
445  buffer = NULL;
446  BytesReturned = 0;
447  ChannelEvent = NULL;
448  s = Stream_New(NULL, 4096);
449 
450  if (!s)
451  {
452  WLog_ERR(TAG, "Stream_New failed!");
453  error = CHANNEL_RC_NO_MEMORY;
454  goto out;
455  }
456 
457  if (WTSVirtualChannelQuery(context->priv->ChannelHandle, WTSVirtualEventHandle, &buffer,
458  &BytesReturned) == TRUE)
459  {
460  if (BytesReturned == sizeof(HANDLE))
461  ChannelEvent = *(HANDLE*)buffer;
462 
463  WTSFreeMemory(buffer);
464  }
465  else
466  {
467  WLog_ERR(TAG, "WTSVirtualChannelQuery failed!");
468  error = ERROR_INTERNAL_ERROR;
469  goto out;
470  }
471 
472  nCount = 0;
473  events[nCount++] = ChannelEvent;
474  events[nCount++] = context->priv->StopEvent;
475 
476  if ((error = remdesk_send_ctl_version_info_pdu(context)))
477  {
478  WLog_ERR(TAG, "remdesk_send_ctl_version_info_pdu failed with error %" PRIu32 "!", error);
479  goto out;
480  }
481 
482  while (1)
483  {
484  status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
485 
486  if (status == WAIT_FAILED)
487  {
488  error = GetLastError();
489  WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
490  break;
491  }
492 
493  status = WaitForSingleObject(context->priv->StopEvent, 0);
494 
495  if (status == WAIT_FAILED)
496  {
497  error = GetLastError();
498  WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
499  break;
500  }
501 
502  if (status == WAIT_OBJECT_0)
503  {
504  break;
505  }
506 
507  const size_t len = Stream_Capacity(s);
508  if (len > UINT32_MAX)
509  {
510  error = ERROR_INTERNAL_ERROR;
511  break;
512  }
513  if (WTSVirtualChannelRead(context->priv->ChannelHandle, 0, Stream_BufferAs(s, char),
514  (UINT32)len, &BytesReturned))
515  {
516  if (BytesReturned)
517  Stream_Seek(s, BytesReturned);
518  }
519  else
520  {
521  if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
522  {
523  WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
524  error = CHANNEL_RC_NO_MEMORY;
525  break;
526  }
527  }
528 
529  if (Stream_GetPosition(s) >= 8)
530  {
531  pHeader = Stream_BufferAs(s, UINT32);
532  PduLength = pHeader[0] + pHeader[1] + 8;
533 
534  if (PduLength >= Stream_GetPosition(s))
535  {
536  Stream_SealLength(s);
537  Stream_SetPosition(s, 0);
538 
539  if ((error = remdesk_server_receive_pdu(context, s)))
540  {
541  WLog_ERR(TAG, "remdesk_server_receive_pdu failed with error %" PRIu32 "!",
542  error);
543  break;
544  }
545 
546  Stream_SetPosition(s, 0);
547  }
548  }
549  }
550 
551  Stream_Free(s, TRUE);
552 out:
553 
554  if (error && context->rdpcontext)
555  setChannelError(context->rdpcontext, error, "remdesk_server_thread reported an error");
556 
557  ExitThread(error);
558  return error;
559 }
560 
566 static UINT remdesk_server_start(RemdeskServerContext* context)
567 {
568  context->priv->ChannelHandle =
569  WTSVirtualChannelOpen(context->vcm, WTS_CURRENT_SESSION, REMDESK_SVC_CHANNEL_NAME);
570 
571  if (!context->priv->ChannelHandle)
572  {
573  WLog_ERR(TAG, "WTSVirtualChannelOpen failed!");
574  return ERROR_INTERNAL_ERROR;
575  }
576 
577  if (!(context->priv->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
578  {
579  WLog_ERR(TAG, "CreateEvent failed!");
580  return ERROR_INTERNAL_ERROR;
581  }
582 
583  if (!(context->priv->Thread =
584  CreateThread(NULL, 0, remdesk_server_thread, (void*)context, 0, NULL)))
585  {
586  WLog_ERR(TAG, "CreateThread failed!");
587  (void)CloseHandle(context->priv->StopEvent);
588  context->priv->StopEvent = NULL;
589  return ERROR_INTERNAL_ERROR;
590  }
591 
592  return CHANNEL_RC_OK;
593 }
594 
600 static UINT remdesk_server_stop(RemdeskServerContext* context)
601 {
602  UINT error = 0;
603  (void)SetEvent(context->priv->StopEvent);
604 
605  if (WaitForSingleObject(context->priv->Thread, INFINITE) == WAIT_FAILED)
606  {
607  error = GetLastError();
608  WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error);
609  return error;
610  }
611 
612  (void)CloseHandle(context->priv->Thread);
613  (void)CloseHandle(context->priv->StopEvent);
614  return CHANNEL_RC_OK;
615 }
616 
617 RemdeskServerContext* remdesk_server_context_new(HANDLE vcm)
618 {
619  RemdeskServerContext* context = NULL;
620  context = (RemdeskServerContext*)calloc(1, sizeof(RemdeskServerContext));
621 
622  if (context)
623  {
624  context->vcm = vcm;
625  context->Start = remdesk_server_start;
626  context->Stop = remdesk_server_stop;
627  context->priv = (RemdeskServerPrivate*)calloc(1, sizeof(RemdeskServerPrivate));
628 
629  if (!context->priv)
630  {
631  free(context);
632  return NULL;
633  }
634 
635  context->priv->Version = 1;
636  }
637 
638  return context;
639 }
640 
641 void remdesk_server_context_free(RemdeskServerContext* context)
642 {
643  if (context)
644  {
645  if (context->priv->ChannelHandle != INVALID_HANDLE_VALUE)
646  (void)WTSVirtualChannelClose(context->priv->ChannelHandle);
647 
648  free(context->priv);
649  free(context);
650  }
651 }