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