Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/channels/remdesk/client/remdesk_main.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Remote Assistance Virtual Channel
4
 *
5
 * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 * Copyright 2015 Thincast Technologies GmbH
7
 * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.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 <winpr/crt.h>
25
#include <winpr/assert.h>
26
#include <winpr/print.h>
27
28
#include <freerdp/freerdp.h>
29
#include <freerdp/assistance.h>
30
31
#include <freerdp/channels/log.h>
32
#include <freerdp/client/remdesk.h>
33
34
#include "remdesk_main.h"
35
#include "remdesk_common.h"
36
37
/**
38
 * Function description
39
 *
40
 * @return 0 on success, otherwise a Win32 error code
41
 */
42
static UINT remdesk_virtual_channel_write(remdeskPlugin* remdesk, wStream* s)
43
0
{
44
0
  UINT32 status = 0;
45
46
0
  if (!remdesk)
47
0
  {
48
0
    WLog_ERR(TAG, "remdesk was null!");
49
0
    Stream_Free(s, TRUE);
50
0
    return CHANNEL_RC_INVALID_INSTANCE;
51
0
  }
52
53
0
  WINPR_ASSERT(remdesk->channelEntryPoints.pVirtualChannelWriteEx);
54
0
  status = remdesk->channelEntryPoints.pVirtualChannelWriteEx(
55
0
      remdesk->InitHandle, remdesk->OpenHandle, Stream_Buffer(s), (UINT32)Stream_Length(s), s);
56
57
0
  if (status != CHANNEL_RC_OK)
58
0
  {
59
0
    Stream_Free(s, TRUE);
60
0
    WLog_ERR(TAG, "pVirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
61
0
             WTSErrorToString(status), status);
62
0
  }
63
0
  return status;
64
0
}
65
66
/**
67
 * Function description
68
 *
69
 * @return 0 on success, otherwise a Win32 error code
70
 */
71
static UINT remdesk_generate_expert_blob(remdeskPlugin* remdesk)
72
0
{
73
0
  const char* name = NULL;
74
0
  char* pass = NULL;
75
0
  const char* password = NULL;
76
0
  rdpSettings* settings = NULL;
77
78
0
  WINPR_ASSERT(remdesk);
79
80
0
  WINPR_ASSERT(remdesk->rdpcontext);
81
0
  settings = remdesk->rdpcontext->settings;
82
0
  WINPR_ASSERT(settings);
83
84
0
  if (remdesk->ExpertBlob)
85
0
    return CHANNEL_RC_OK;
86
87
0
  password = freerdp_settings_get_string(settings, FreeRDP_RemoteAssistancePassword);
88
0
  if (!password)
89
0
    password = freerdp_settings_get_string(settings, FreeRDP_Password);
90
91
0
  if (!password)
92
0
  {
93
0
    WLog_ERR(TAG, "password was not set!");
94
0
    return ERROR_INTERNAL_ERROR;
95
0
  }
96
97
0
  name = freerdp_settings_get_string(settings, FreeRDP_Username);
98
99
0
  if (!name)
100
0
    name = "Expert";
101
102
0
  const char* stub = freerdp_settings_get_string(settings, FreeRDP_RemoteAssistancePassStub);
103
0
  remdesk->EncryptedPassStub =
104
0
      freerdp_assistance_encrypt_pass_stub(password, stub, &(remdesk->EncryptedPassStubSize));
105
106
0
  if (!remdesk->EncryptedPassStub)
107
0
  {
108
0
    WLog_ERR(TAG, "freerdp_assistance_encrypt_pass_stub failed!");
109
0
    return ERROR_INTERNAL_ERROR;
110
0
  }
111
112
0
  pass = freerdp_assistance_bin_to_hex_string(remdesk->EncryptedPassStub,
113
0
                                              remdesk->EncryptedPassStubSize);
114
115
0
  if (!pass)
116
0
  {
117
0
    WLog_ERR(TAG, "freerdp_assistance_bin_to_hex_string failed!");
118
0
    return ERROR_INTERNAL_ERROR;
119
0
  }
120
121
0
  remdesk->ExpertBlob = freerdp_assistance_construct_expert_blob(name, pass);
122
0
  free(pass);
123
124
0
  if (!remdesk->ExpertBlob)
125
0
  {
126
0
    WLog_ERR(TAG, "freerdp_assistance_construct_expert_blob failed!");
127
0
    return ERROR_INTERNAL_ERROR;
128
0
  }
129
130
0
  return CHANNEL_RC_OK;
131
0
}
132
133
/**
134
 * Function description
135
 *
136
 * @return 0 on success, otherwise a Win32 error code
137
 */
138
static UINT remdesk_recv_ctl_server_announce_pdu(WINPR_ATTR_UNUSED remdeskPlugin* remdesk,
139
                                                 WINPR_ATTR_UNUSED wStream* s,
140
                                                 WINPR_ATTR_UNUSED REMDESK_CHANNEL_HEADER* header)
141
0
{
142
0
  WINPR_ASSERT(remdesk);
143
0
  WINPR_ASSERT(s);
144
0
  WINPR_ASSERT(header);
145
146
0
  WLog_ERR("TODO", "TODO: implement");
147
0
  return CHANNEL_RC_OK;
148
0
}
149
150
/**
151
 * Function description
152
 *
153
 * @return 0 on success, otherwise a Win32 error code
154
 */
155
static UINT remdesk_recv_ctl_version_info_pdu(remdeskPlugin* remdesk, wStream* s,
156
                                              WINPR_ATTR_UNUSED REMDESK_CHANNEL_HEADER* header)
157
0
{
158
0
  UINT32 versionMajor = 0;
159
0
  UINT32 versionMinor = 0;
160
161
0
  WINPR_ASSERT(remdesk);
162
0
  WINPR_ASSERT(s);
163
0
  WINPR_ASSERT(header);
164
165
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
166
0
    return ERROR_INVALID_DATA;
167
168
0
  Stream_Read_UINT32(s, versionMajor); /* versionMajor (4 bytes) */
169
0
  Stream_Read_UINT32(s, versionMinor); /* versionMinor (4 bytes) */
170
171
0
  if ((versionMajor != 1) || (versionMinor > 2) || (versionMinor == 0))
172
0
  {
173
0
    WLog_ERR(TAG, "Unsupported protocol version %" PRId32 ".%" PRId32, versionMajor,
174
0
             versionMinor);
175
0
  }
176
177
0
  remdesk->Version = versionMinor;
178
0
  return CHANNEL_RC_OK;
179
0
}
180
181
/**
182
 * Function description
183
 *
184
 * @return 0 on success, otherwise a Win32 error code
185
 */
186
static UINT remdesk_send_ctl_version_info_pdu(remdeskPlugin* remdesk)
187
0
{
188
0
  REMDESK_CTL_VERSION_INFO_PDU pdu = { 0 };
189
190
0
  WINPR_ASSERT(remdesk);
191
192
0
  UINT error = remdesk_prepare_ctl_header(&(pdu.ctlHeader), REMDESK_CTL_VERSIONINFO, 8);
193
0
  if (error)
194
0
    return error;
195
196
0
  pdu.versionMajor = 1;
197
0
  pdu.versionMinor = 2;
198
0
  wStream* s = Stream_New(NULL, REMDESK_CHANNEL_CTL_SIZE + pdu.ctlHeader.ch.DataLength);
199
200
0
  if (!s)
201
0
  {
202
0
    WLog_ERR(TAG, "Stream_New failed!");
203
0
    return CHANNEL_RC_NO_MEMORY;
204
0
  }
205
206
0
  error = remdesk_write_ctl_header(s, &(pdu.ctlHeader));
207
0
  if (error)
208
0
  {
209
0
    Stream_Free(s, TRUE);
210
0
    return error;
211
0
  }
212
0
  Stream_Write_UINT32(s, pdu.versionMajor); /* versionMajor (4 bytes) */
213
0
  Stream_Write_UINT32(s, pdu.versionMinor); /* versionMinor (4 bytes) */
214
0
  Stream_SealLength(s);
215
216
0
  if ((error = remdesk_virtual_channel_write(remdesk, s)))
217
0
    WLog_ERR(TAG, "remdesk_virtual_channel_write failed with error %" PRIu32 "!", error);
218
219
0
  return error;
220
0
}
221
222
/**
223
 * Function description
224
 *
225
 * @return 0 on success, otherwise a Win32 error code
226
 */
227
static UINT remdesk_recv_ctl_result_pdu(WINPR_ATTR_UNUSED remdeskPlugin* remdesk, wStream* s,
228
                                        WINPR_ATTR_UNUSED REMDESK_CHANNEL_HEADER* header,
229
                                        UINT32* pResult)
230
0
{
231
0
  UINT32 result = 0;
232
233
0
  WINPR_ASSERT(remdesk);
234
0
  WINPR_ASSERT(s);
235
0
  WINPR_ASSERT(header);
236
0
  WINPR_ASSERT(pResult);
237
238
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
239
0
    return ERROR_INVALID_DATA;
240
241
0
  Stream_Read_UINT32(s, result); /* result (4 bytes) */
242
0
  *pResult = result;
243
  // WLog_DBG(TAG, "RemdeskRecvResult: 0x%08"PRIX32"", result);
244
0
  switch (result)
245
0
  {
246
0
    case REMDESK_ERROR_HELPEESAIDNO:
247
0
      WLog_DBG(TAG, "remote assistance connection request was denied");
248
0
      return ERROR_CONNECTION_REFUSED;
249
250
0
    default:
251
0
      break;
252
0
  }
253
254
0
  return CHANNEL_RC_OK;
255
0
}
256
257
/**
258
 * Function description
259
 *
260
 * @return 0 on success, otherwise a Win32 error code
261
 */
262
static UINT remdesk_send_ctl_authenticate_pdu(remdeskPlugin* remdesk)
263
0
{
264
0
  UINT error = ERROR_INTERNAL_ERROR;
265
0
  size_t cbExpertBlobW = 0;
266
0
  WCHAR* expertBlobW = NULL;
267
0
  size_t cbRaConnectionStringW = 0;
268
0
  REMDESK_CTL_HEADER ctlHeader = { 0 };
269
270
0
  WINPR_ASSERT(remdesk);
271
272
0
  if ((error = remdesk_generate_expert_blob(remdesk)))
273
0
  {
274
0
    WLog_ERR(TAG, "remdesk_generate_expert_blob failed with error %" PRIu32 "", error);
275
0
    return error;
276
0
  }
277
278
0
  const char* expertBlob = remdesk->ExpertBlob;
279
0
  WINPR_ASSERT(remdesk->rdpcontext);
280
0
  rdpSettings* settings = remdesk->rdpcontext->settings;
281
0
  WINPR_ASSERT(settings);
282
283
0
  const char* raConnectionString =
284
0
      freerdp_settings_get_string(settings, FreeRDP_RemoteAssistanceRCTicket);
285
0
  WCHAR* raConnectionStringW =
286
0
      ConvertUtf8ToWCharAlloc(raConnectionString, &cbRaConnectionStringW);
287
288
0
  if (!raConnectionStringW || (cbRaConnectionStringW > UINT32_MAX / sizeof(WCHAR)))
289
0
    goto out;
290
291
0
  cbRaConnectionStringW = cbRaConnectionStringW * sizeof(WCHAR);
292
293
0
  expertBlobW = ConvertUtf8ToWCharAlloc(expertBlob, &cbExpertBlobW);
294
295
0
  if (!expertBlobW)
296
0
    goto out;
297
298
0
  cbExpertBlobW = cbExpertBlobW * sizeof(WCHAR);
299
0
  error = remdesk_prepare_ctl_header(&(ctlHeader), REMDESK_CTL_AUTHENTICATE,
300
0
                                     cbRaConnectionStringW + cbExpertBlobW);
301
0
  if (error)
302
0
    goto out;
303
304
0
  wStream* s = Stream_New(NULL, REMDESK_CHANNEL_CTL_SIZE + ctlHeader.ch.DataLength);
305
306
0
  if (!s)
307
0
  {
308
0
    WLog_ERR(TAG, "Stream_New failed!");
309
0
    error = CHANNEL_RC_NO_MEMORY;
310
0
    goto out;
311
0
  }
312
313
0
  error = remdesk_write_ctl_header(s, &ctlHeader);
314
0
  if (error)
315
0
  {
316
0
    Stream_Free(s, TRUE);
317
0
    goto out;
318
0
  }
319
0
  Stream_Write(s, raConnectionStringW, cbRaConnectionStringW);
320
0
  Stream_Write(s, expertBlobW, cbExpertBlobW);
321
0
  Stream_SealLength(s);
322
323
0
  error = remdesk_virtual_channel_write(remdesk, s);
324
0
  if (error)
325
0
    WLog_ERR(TAG, "remdesk_virtual_channel_write failed with error %" PRIu32 "!", error);
326
327
0
out:
328
0
  free(raConnectionStringW);
329
0
  free(expertBlobW);
330
331
0
  return error;
332
0
}
333
334
/**
335
 * Function description
336
 *
337
 * @return 0 on success, otherwise a Win32 error code
338
 */
339
static UINT remdesk_send_ctl_remote_control_desktop_pdu(remdeskPlugin* remdesk)
340
0
{
341
0
  UINT error = 0;
342
0
  size_t length = 0;
343
344
0
  WINPR_ASSERT(remdesk);
345
0
  WINPR_ASSERT(remdesk->rdpcontext);
346
0
  rdpSettings* settings = remdesk->rdpcontext->settings;
347
0
  WINPR_ASSERT(settings);
348
349
0
  const char* raConnectionString =
350
0
      freerdp_settings_get_string(settings, FreeRDP_RemoteAssistanceRCTicket);
351
0
  WCHAR* raConnectionStringW = ConvertUtf8ToWCharAlloc(raConnectionString, &length);
352
0
  size_t cbRaConnectionStringW = length * sizeof(WCHAR);
353
354
0
  if (!raConnectionStringW)
355
0
    return ERROR_INTERNAL_ERROR;
356
357
0
  REMDESK_CTL_HEADER ctlHeader = { 0 };
358
0
  error = remdesk_prepare_ctl_header(&ctlHeader, REMDESK_CTL_REMOTE_CONTROL_DESKTOP,
359
0
                                     cbRaConnectionStringW);
360
0
  if (error != CHANNEL_RC_OK)
361
0
    goto out;
362
363
0
  wStream* s = Stream_New(NULL, REMDESK_CHANNEL_CTL_SIZE + ctlHeader.ch.DataLength);
364
365
0
  if (!s)
366
0
  {
367
0
    WLog_ERR(TAG, "Stream_New failed!");
368
0
    error = CHANNEL_RC_NO_MEMORY;
369
0
    goto out;
370
0
  }
371
372
0
  error = remdesk_write_ctl_header(s, &ctlHeader);
373
0
  if (error)
374
0
  {
375
0
    Stream_Free(s, TRUE);
376
0
    goto out;
377
0
  }
378
0
  Stream_Write(s, raConnectionStringW, cbRaConnectionStringW);
379
0
  Stream_SealLength(s);
380
381
0
  if ((error = remdesk_virtual_channel_write(remdesk, s)))
382
0
    WLog_ERR(TAG, "remdesk_virtual_channel_write failed with error %" PRIu32 "!", error);
383
384
0
out:
385
0
  free(raConnectionStringW);
386
387
0
  return error;
388
0
}
389
390
/**
391
 * Function description
392
 *
393
 * @return 0 on success, otherwise a Win32 error code
394
 */
395
static UINT remdesk_send_ctl_verify_password_pdu(remdeskPlugin* remdesk)
396
0
{
397
0
  size_t cbExpertBlobW = 0;
398
0
  REMDESK_CTL_VERIFY_PASSWORD_PDU pdu = { 0 };
399
400
0
  WINPR_ASSERT(remdesk);
401
402
0
  UINT error = remdesk_generate_expert_blob(remdesk);
403
0
  if (error)
404
0
  {
405
0
    WLog_ERR(TAG, "remdesk_generate_expert_blob failed with error %" PRIu32 "!", error);
406
0
    return error;
407
0
  }
408
409
0
  pdu.expertBlob = remdesk->ExpertBlob;
410
0
  WCHAR* expertBlobW = ConvertUtf8ToWCharAlloc(pdu.expertBlob, &cbExpertBlobW);
411
412
0
  if (!expertBlobW)
413
0
    goto out;
414
415
0
  cbExpertBlobW = cbExpertBlobW * sizeof(WCHAR);
416
0
  error =
417
0
      remdesk_prepare_ctl_header(&(pdu.ctlHeader), REMDESK_CTL_VERIFY_PASSWORD, cbExpertBlobW);
418
0
  if (error)
419
0
    goto out;
420
421
0
  wStream* s = Stream_New(NULL, 1ULL * REMDESK_CHANNEL_CTL_SIZE + pdu.ctlHeader.ch.DataLength);
422
423
0
  if (!s)
424
0
  {
425
0
    WLog_ERR(TAG, "Stream_New failed!");
426
0
    error = CHANNEL_RC_NO_MEMORY;
427
0
    goto out;
428
0
  }
429
430
0
  error = remdesk_write_ctl_header(s, &(pdu.ctlHeader));
431
0
  if (error)
432
0
  {
433
0
    Stream_Free(s, TRUE);
434
0
    goto out;
435
0
  }
436
0
  Stream_Write(s, expertBlobW, cbExpertBlobW);
437
0
  Stream_SealLength(s);
438
439
0
  error = remdesk_virtual_channel_write(remdesk, s);
440
0
  if (error)
441
0
    WLog_ERR(TAG, "remdesk_virtual_channel_write failed with error %" PRIu32 "!", error);
442
443
0
out:
444
0
  free(expertBlobW);
445
446
0
  return error;
447
0
}
448
449
/**
450
 * Function description
451
 *
452
 * @return 0 on success, otherwise a Win32 error code
453
 */
454
static UINT remdesk_send_ctl_expert_on_vista_pdu(remdeskPlugin* remdesk)
455
0
{
456
0
  REMDESK_CTL_EXPERT_ON_VISTA_PDU pdu = { 0 };
457
458
0
  WINPR_ASSERT(remdesk);
459
460
0
  UINT error = remdesk_generate_expert_blob(remdesk);
461
0
  if (error)
462
0
  {
463
0
    WLog_ERR(TAG, "remdesk_generate_expert_blob failed with error %" PRIu32 "!", error);
464
0
    return error;
465
0
  }
466
0
  if (remdesk->EncryptedPassStubSize > UINT32_MAX)
467
0
    return ERROR_INTERNAL_ERROR;
468
469
0
  pdu.EncryptedPasswordLength = (UINT32)remdesk->EncryptedPassStubSize;
470
0
  pdu.EncryptedPassword = remdesk->EncryptedPassStub;
471
0
  error = remdesk_prepare_ctl_header(&(pdu.ctlHeader), REMDESK_CTL_EXPERT_ON_VISTA,
472
0
                                     pdu.EncryptedPasswordLength);
473
0
  if (error)
474
0
    return error;
475
476
0
  wStream* s = Stream_New(NULL, REMDESK_CHANNEL_CTL_SIZE + pdu.ctlHeader.ch.DataLength);
477
478
0
  if (!s)
479
0
  {
480
0
    WLog_ERR(TAG, "Stream_New failed!");
481
0
    return CHANNEL_RC_NO_MEMORY;
482
0
  }
483
484
0
  error = remdesk_write_ctl_header(s, &(pdu.ctlHeader));
485
0
  if (error)
486
0
  {
487
0
    Stream_Free(s, TRUE);
488
0
    return error;
489
0
  }
490
0
  Stream_Write(s, pdu.EncryptedPassword, pdu.EncryptedPasswordLength);
491
0
  Stream_SealLength(s);
492
0
  return remdesk_virtual_channel_write(remdesk, s);
493
0
}
494
495
/**
496
 * Function description
497
 *
498
 * @return 0 on success, otherwise a Win32 error code
499
 */
500
static UINT remdesk_recv_ctl_pdu(remdeskPlugin* remdesk, wStream* s, REMDESK_CHANNEL_HEADER* header)
501
0
{
502
0
  UINT error = CHANNEL_RC_OK;
503
0
  UINT32 msgType = 0;
504
0
  UINT32 result = 0;
505
506
0
  WINPR_ASSERT(remdesk);
507
0
  WINPR_ASSERT(s);
508
0
  WINPR_ASSERT(header);
509
510
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
511
0
    return ERROR_INVALID_DATA;
512
513
0
  Stream_Read_UINT32(s, msgType); /* msgType (4 bytes) */
514
515
  // WLog_DBG(TAG, "msgType: %"PRIu32"", msgType);
516
517
0
  switch (msgType)
518
0
  {
519
0
    case REMDESK_CTL_REMOTE_CONTROL_DESKTOP:
520
0
      break;
521
522
0
    case REMDESK_CTL_RESULT:
523
0
      if ((error = remdesk_recv_ctl_result_pdu(remdesk, s, header, &result)))
524
0
        WLog_ERR(TAG, "remdesk_recv_ctl_result_pdu failed with error %" PRIu32 "", error);
525
526
0
      break;
527
528
0
    case REMDESK_CTL_AUTHENTICATE:
529
0
      break;
530
531
0
    case REMDESK_CTL_SERVER_ANNOUNCE:
532
0
      if ((error = remdesk_recv_ctl_server_announce_pdu(remdesk, s, header)))
533
0
        WLog_ERR(TAG, "remdesk_recv_ctl_server_announce_pdu failed with error %" PRIu32 "",
534
0
                 error);
535
536
0
      break;
537
538
0
    case REMDESK_CTL_DISCONNECT:
539
0
      break;
540
541
0
    case REMDESK_CTL_VERSIONINFO:
542
0
      if ((error = remdesk_recv_ctl_version_info_pdu(remdesk, s, header)))
543
0
      {
544
0
        WLog_ERR(TAG, "remdesk_recv_ctl_version_info_pdu failed with error %" PRIu32 "",
545
0
                 error);
546
0
        break;
547
0
      }
548
549
0
      if (remdesk->Version == 1)
550
0
      {
551
0
        if ((error = remdesk_send_ctl_version_info_pdu(remdesk)))
552
0
        {
553
0
          WLog_ERR(TAG, "remdesk_send_ctl_version_info_pdu failed with error %" PRIu32 "",
554
0
                   error);
555
0
          break;
556
0
        }
557
558
0
        if ((error = remdesk_send_ctl_authenticate_pdu(remdesk)))
559
0
        {
560
0
          WLog_ERR(TAG, "remdesk_send_ctl_authenticate_pdu failed with error %" PRIu32 "",
561
0
                   error);
562
0
          break;
563
0
        }
564
565
0
        if ((error = remdesk_send_ctl_remote_control_desktop_pdu(remdesk)))
566
0
        {
567
0
          WLog_ERR(
568
0
              TAG,
569
0
              "remdesk_send_ctl_remote_control_desktop_pdu failed with error %" PRIu32 "",
570
0
              error);
571
0
          break;
572
0
        }
573
0
      }
574
0
      else if (remdesk->Version == 2)
575
0
      {
576
0
        if ((error = remdesk_send_ctl_expert_on_vista_pdu(remdesk)))
577
0
        {
578
0
          WLog_ERR(TAG,
579
0
                   "remdesk_send_ctl_expert_on_vista_pdu failed with error %" PRIu32 "",
580
0
                   error);
581
0
          break;
582
0
        }
583
584
0
        if ((error = remdesk_send_ctl_verify_password_pdu(remdesk)))
585
0
        {
586
0
          WLog_ERR(TAG,
587
0
                   "remdesk_send_ctl_verify_password_pdu failed with error %" PRIu32 "",
588
0
                   error);
589
0
          break;
590
0
        }
591
0
      }
592
593
0
      break;
594
595
0
    case REMDESK_CTL_ISCONNECTED:
596
0
      break;
597
598
0
    case REMDESK_CTL_VERIFY_PASSWORD:
599
0
      break;
600
601
0
    case REMDESK_CTL_EXPERT_ON_VISTA:
602
0
      break;
603
604
0
    case REMDESK_CTL_RANOVICE_NAME:
605
0
      break;
606
607
0
    case REMDESK_CTL_RAEXPERT_NAME:
608
0
      break;
609
610
0
    case REMDESK_CTL_TOKEN:
611
0
      break;
612
613
0
    default:
614
0
      WLog_ERR(TAG, "unknown msgType: %" PRIu32 "", msgType);
615
0
      error = ERROR_INVALID_DATA;
616
0
      break;
617
0
  }
618
619
0
  return error;
620
0
}
621
622
/**
623
 * Function description
624
 *
625
 * @return 0 on success, otherwise a Win32 error code
626
 */
627
static UINT remdesk_process_receive(remdeskPlugin* remdesk, wStream* s)
628
0
{
629
0
  UINT status = 0;
630
0
  REMDESK_CHANNEL_HEADER header;
631
632
0
  WINPR_ASSERT(remdesk);
633
0
  WINPR_ASSERT(s);
634
635
0
  if ((status = remdesk_read_channel_header(s, &header)))
636
0
  {
637
0
    WLog_ERR(TAG, "remdesk_read_channel_header failed with error %" PRIu32 "", status);
638
0
    return status;
639
0
  }
640
641
0
  if (strcmp(header.ChannelName, "RC_CTL") == 0)
642
0
  {
643
0
    status = remdesk_recv_ctl_pdu(remdesk, s, &header);
644
0
  }
645
0
  else if (strcmp(header.ChannelName, "70") == 0)
646
0
  {
647
0
  }
648
0
  else if (strcmp(header.ChannelName, "71") == 0)
649
0
  {
650
0
  }
651
0
  else if (strcmp(header.ChannelName, ".") == 0)
652
0
  {
653
0
  }
654
0
  else if (strcmp(header.ChannelName, "1000.") == 0)
655
0
  {
656
0
  }
657
0
  else if (strcmp(header.ChannelName, "RA_FX") == 0)
658
0
  {
659
0
  }
660
0
  else
661
0
  {
662
0
  }
663
664
0
  return status;
665
0
}
666
667
static void remdesk_process_connect(WINPR_ATTR_UNUSED remdeskPlugin* remdesk)
668
0
{
669
0
  WINPR_ASSERT(remdesk);
670
0
  WLog_ERR("TODO", "TODO: implement");
671
0
}
672
673
/**
674
 * Function description
675
 *
676
 * @return 0 on success, otherwise a Win32 error code
677
 */
678
static UINT remdesk_virtual_channel_event_data_received(remdeskPlugin* remdesk, const void* pData,
679
                                                        UINT32 dataLength, UINT32 totalLength,
680
                                                        UINT32 dataFlags)
681
0
{
682
0
  wStream* data_in = NULL;
683
684
0
  WINPR_ASSERT(remdesk);
685
686
0
  if ((dataFlags & CHANNEL_FLAG_SUSPEND) || (dataFlags & CHANNEL_FLAG_RESUME))
687
0
  {
688
0
    return CHANNEL_RC_OK;
689
0
  }
690
691
0
  if (dataFlags & CHANNEL_FLAG_FIRST)
692
0
  {
693
0
    if (remdesk->data_in)
694
0
      Stream_Free(remdesk->data_in, TRUE);
695
696
0
    remdesk->data_in = Stream_New(NULL, totalLength);
697
698
0
    if (!remdesk->data_in)
699
0
    {
700
0
      WLog_ERR(TAG, "Stream_New failed!");
701
0
      return CHANNEL_RC_NO_MEMORY;
702
0
    }
703
0
  }
704
705
0
  data_in = remdesk->data_in;
706
707
0
  if (!Stream_EnsureRemainingCapacity(data_in, dataLength))
708
0
  {
709
0
    WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
710
0
    return CHANNEL_RC_NO_MEMORY;
711
0
  }
712
713
0
  Stream_Write(data_in, pData, dataLength);
714
715
0
  if (dataFlags & CHANNEL_FLAG_LAST)
716
0
  {
717
0
    if (Stream_Capacity(data_in) != Stream_GetPosition(data_in))
718
0
    {
719
0
      WLog_ERR(TAG, "read error");
720
0
      return ERROR_INTERNAL_ERROR;
721
0
    }
722
723
0
    remdesk->data_in = NULL;
724
0
    Stream_SealLength(data_in);
725
0
    Stream_SetPosition(data_in, 0);
726
727
0
    if (!MessageQueue_Post(remdesk->queue, NULL, 0, (void*)data_in, NULL))
728
0
    {
729
0
      WLog_ERR(TAG, "MessageQueue_Post failed!");
730
0
      return ERROR_INTERNAL_ERROR;
731
0
    }
732
0
  }
733
734
0
  return CHANNEL_RC_OK;
735
0
}
736
737
static VOID VCAPITYPE remdesk_virtual_channel_open_event_ex(LPVOID lpUserParam, DWORD openHandle,
738
                                                            UINT event, LPVOID pData,
739
                                                            UINT32 dataLength, UINT32 totalLength,
740
                                                            UINT32 dataFlags)
741
0
{
742
0
  UINT error = CHANNEL_RC_OK;
743
0
  remdeskPlugin* remdesk = (remdeskPlugin*)lpUserParam;
744
745
0
  switch (event)
746
0
  {
747
0
    case CHANNEL_EVENT_INITIALIZED:
748
0
      break;
749
750
0
    case CHANNEL_EVENT_DATA_RECEIVED:
751
0
      if (!remdesk || (remdesk->OpenHandle != openHandle))
752
0
      {
753
0
        WLog_ERR(TAG, "error no match");
754
0
        return;
755
0
      }
756
0
      if ((error = remdesk_virtual_channel_event_data_received(remdesk, pData, dataLength,
757
0
                                                               totalLength, dataFlags)))
758
0
        WLog_ERR(TAG,
759
0
                 "remdesk_virtual_channel_event_data_received failed with error %" PRIu32
760
0
                 "!",
761
0
                 error);
762
763
0
      break;
764
765
0
    case CHANNEL_EVENT_WRITE_CANCELLED:
766
0
    case CHANNEL_EVENT_WRITE_COMPLETE:
767
0
    {
768
0
      wStream* s = (wStream*)pData;
769
0
      Stream_Free(s, TRUE);
770
0
    }
771
0
    break;
772
773
0
    case CHANNEL_EVENT_USER:
774
0
      break;
775
776
0
    default:
777
0
      WLog_ERR(TAG, "unhandled event %" PRIu32 "!", event);
778
0
      error = ERROR_INTERNAL_ERROR;
779
0
      break;
780
0
  }
781
782
0
  if (error && remdesk && remdesk->rdpcontext)
783
0
    setChannelError(remdesk->rdpcontext, error,
784
0
                    "remdesk_virtual_channel_open_event_ex reported an error");
785
0
}
786
787
static DWORD WINAPI remdesk_virtual_channel_client_thread(LPVOID arg)
788
0
{
789
0
  wStream* data = NULL;
790
0
  wMessage message = { 0 };
791
0
  remdeskPlugin* remdesk = (remdeskPlugin*)arg;
792
0
  UINT error = CHANNEL_RC_OK;
793
794
0
  WINPR_ASSERT(remdesk);
795
796
0
  remdesk_process_connect(remdesk);
797
798
0
  while (1)
799
0
  {
800
0
    if (!MessageQueue_Wait(remdesk->queue))
801
0
    {
802
0
      WLog_ERR(TAG, "MessageQueue_Wait failed!");
803
0
      error = ERROR_INTERNAL_ERROR;
804
0
      break;
805
0
    }
806
807
0
    if (!MessageQueue_Peek(remdesk->queue, &message, TRUE))
808
0
    {
809
0
      WLog_ERR(TAG, "MessageQueue_Peek failed!");
810
0
      error = ERROR_INTERNAL_ERROR;
811
0
      break;
812
0
    }
813
814
0
    if (message.id == WMQ_QUIT)
815
0
      break;
816
817
0
    if (message.id == 0)
818
0
    {
819
0
      data = (wStream*)message.wParam;
820
821
0
      if ((error = remdesk_process_receive(remdesk, data)))
822
0
      {
823
0
        WLog_ERR(TAG, "remdesk_process_receive failed with error %" PRIu32 "!", error);
824
0
        Stream_Free(data, TRUE);
825
0
        break;
826
0
      }
827
828
0
      Stream_Free(data, TRUE);
829
0
    }
830
0
  }
831
832
0
  if (error && remdesk->rdpcontext)
833
0
    setChannelError(remdesk->rdpcontext, error,
834
0
                    "remdesk_virtual_channel_client_thread reported an error");
835
836
0
  ExitThread(error);
837
0
  return error;
838
0
}
839
840
/**
841
 * Function description
842
 *
843
 * @return 0 on success, otherwise a Win32 error code
844
 */
845
static UINT remdesk_virtual_channel_event_connected(remdeskPlugin* remdesk,
846
                                                    WINPR_ATTR_UNUSED LPVOID pData,
847
                                                    WINPR_ATTR_UNUSED UINT32 dataLength)
848
0
{
849
0
  UINT error = 0;
850
851
0
  WINPR_ASSERT(remdesk);
852
853
0
  remdesk->queue = MessageQueue_New(NULL);
854
855
0
  if (!remdesk->queue)
856
0
  {
857
0
    WLog_ERR(TAG, "MessageQueue_New failed!");
858
0
    error = CHANNEL_RC_NO_MEMORY;
859
0
    goto error_out;
860
0
  }
861
862
0
  remdesk->thread =
863
0
      CreateThread(NULL, 0, remdesk_virtual_channel_client_thread, (void*)remdesk, 0, NULL);
864
865
0
  if (!remdesk->thread)
866
0
  {
867
0
    WLog_ERR(TAG, "CreateThread failed");
868
0
    error = ERROR_INTERNAL_ERROR;
869
0
    goto error_out;
870
0
  }
871
872
0
  return remdesk->channelEntryPoints.pVirtualChannelOpenEx(
873
0
      remdesk->InitHandle, &remdesk->OpenHandle, remdesk->channelDef.name,
874
0
      remdesk_virtual_channel_open_event_ex);
875
0
error_out:
876
0
  MessageQueue_Free(remdesk->queue);
877
0
  remdesk->queue = NULL;
878
0
  return error;
879
0
}
880
881
/**
882
 * Function description
883
 *
884
 * @return 0 on success, otherwise a Win32 error code
885
 */
886
static UINT remdesk_virtual_channel_event_disconnected(remdeskPlugin* remdesk)
887
0
{
888
0
  UINT rc = CHANNEL_RC_OK;
889
890
0
  WINPR_ASSERT(remdesk);
891
892
0
  if (remdesk->queue && remdesk->thread)
893
0
  {
894
0
    if (MessageQueue_PostQuit(remdesk->queue, 0) &&
895
0
        (WaitForSingleObject(remdesk->thread, INFINITE) == WAIT_FAILED))
896
0
    {
897
0
      rc = GetLastError();
898
0
      WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", rc);
899
0
      return rc;
900
0
    }
901
0
  }
902
903
0
  if (remdesk->OpenHandle != 0)
904
0
  {
905
0
    WINPR_ASSERT(remdesk->channelEntryPoints.pVirtualChannelCloseEx);
906
0
    rc = remdesk->channelEntryPoints.pVirtualChannelCloseEx(remdesk->InitHandle,
907
0
                                                            remdesk->OpenHandle);
908
909
0
    if (CHANNEL_RC_OK != rc)
910
0
    {
911
0
      WLog_ERR(TAG, "pVirtualChannelCloseEx failed with %s [%08" PRIX32 "]",
912
0
               WTSErrorToString(rc), rc);
913
0
    }
914
915
0
    remdesk->OpenHandle = 0;
916
0
  }
917
0
  MessageQueue_Free(remdesk->queue);
918
0
  (void)CloseHandle(remdesk->thread);
919
0
  Stream_Free(remdesk->data_in, TRUE);
920
0
  remdesk->data_in = NULL;
921
0
  remdesk->queue = NULL;
922
0
  remdesk->thread = NULL;
923
0
  return rc;
924
0
}
925
926
static void remdesk_virtual_channel_event_terminated(remdeskPlugin* remdesk)
927
0
{
928
0
  WINPR_ASSERT(remdesk);
929
930
0
  remdesk->InitHandle = 0;
931
0
  free(remdesk->context);
932
0
  free(remdesk);
933
0
}
934
935
static VOID VCAPITYPE remdesk_virtual_channel_init_event_ex(LPVOID lpUserParam, LPVOID pInitHandle,
936
                                                            UINT event, LPVOID pData,
937
                                                            UINT dataLength)
938
0
{
939
0
  UINT error = CHANNEL_RC_OK;
940
0
  remdeskPlugin* remdesk = (remdeskPlugin*)lpUserParam;
941
942
0
  if (!remdesk || (remdesk->InitHandle != pInitHandle))
943
0
  {
944
0
    WLog_ERR(TAG, "error no match");
945
0
    return;
946
0
  }
947
948
0
  switch (event)
949
0
  {
950
0
    case CHANNEL_EVENT_CONNECTED:
951
0
      if ((error = remdesk_virtual_channel_event_connected(remdesk, pData, dataLength)))
952
0
        WLog_ERR(TAG,
953
0
                 "remdesk_virtual_channel_event_connected failed with error %" PRIu32 "",
954
0
                 error);
955
956
0
      break;
957
958
0
    case CHANNEL_EVENT_DISCONNECTED:
959
0
      if ((error = remdesk_virtual_channel_event_disconnected(remdesk)))
960
0
        WLog_ERR(TAG,
961
0
                 "remdesk_virtual_channel_event_disconnected failed with error %" PRIu32 "",
962
0
                 error);
963
964
0
      break;
965
966
0
    case CHANNEL_EVENT_TERMINATED:
967
0
      remdesk_virtual_channel_event_terminated(remdesk);
968
0
      break;
969
970
0
    case CHANNEL_EVENT_ATTACHED:
971
0
    case CHANNEL_EVENT_DETACHED:
972
0
    default:
973
0
      break;
974
0
  }
975
976
0
  if (error && remdesk->rdpcontext)
977
0
    setChannelError(remdesk->rdpcontext, error,
978
0
                    "remdesk_virtual_channel_init_event reported an error");
979
0
}
980
981
/* remdesk is always built-in */
982
#define VirtualChannelEntryEx remdesk_VirtualChannelEntryEx
983
984
FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints,
985
                                                         PVOID pInitHandle))
986
0
{
987
0
  UINT rc = 0;
988
0
  remdeskPlugin* remdesk = NULL;
989
0
  RemdeskClientContext* context = NULL;
990
0
  CHANNEL_ENTRY_POINTS_FREERDP_EX* pEntryPointsEx = NULL;
991
992
0
  if (!pEntryPoints)
993
0
  {
994
0
    return FALSE;
995
0
  }
996
997
0
  remdesk = (remdeskPlugin*)calloc(1, sizeof(remdeskPlugin));
998
999
0
  if (!remdesk)
1000
0
  {
1001
0
    WLog_ERR(TAG, "calloc failed!");
1002
0
    return FALSE;
1003
0
  }
1004
1005
0
  remdesk->channelDef.options = CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP |
1006
0
                                CHANNEL_OPTION_COMPRESS_RDP | CHANNEL_OPTION_SHOW_PROTOCOL;
1007
0
  (void)sprintf_s(remdesk->channelDef.name, ARRAYSIZE(remdesk->channelDef.name),
1008
0
                  REMDESK_SVC_CHANNEL_NAME);
1009
0
  remdesk->Version = 2;
1010
0
  pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
1011
1012
0
  if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX)) &&
1013
0
      (pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER))
1014
0
  {
1015
0
    context = (RemdeskClientContext*)calloc(1, sizeof(RemdeskClientContext));
1016
1017
0
    if (!context)
1018
0
    {
1019
0
      WLog_ERR(TAG, "calloc failed!");
1020
0
      goto error_out;
1021
0
    }
1022
1023
0
    context->handle = (void*)remdesk;
1024
0
    remdesk->context = context;
1025
0
    remdesk->rdpcontext = pEntryPointsEx->context;
1026
0
  }
1027
1028
0
  CopyMemory(&(remdesk->channelEntryPoints), pEntryPoints,
1029
0
             sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX));
1030
0
  remdesk->InitHandle = pInitHandle;
1031
0
  rc = remdesk->channelEntryPoints.pVirtualChannelInitEx(
1032
0
      remdesk, context, pInitHandle, &remdesk->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000,
1033
0
      remdesk_virtual_channel_init_event_ex);
1034
1035
0
  if (CHANNEL_RC_OK != rc)
1036
0
  {
1037
0
    WLog_ERR(TAG, "pVirtualChannelInitEx failed with %s [%08" PRIX32 "]", WTSErrorToString(rc),
1038
0
             rc);
1039
0
    goto error_out;
1040
0
  }
1041
1042
0
  remdesk->channelEntryPoints.pInterface = context;
1043
0
  return TRUE;
1044
0
error_out:
1045
0
  free(remdesk);
1046
0
  free(context);
1047
0
  return FALSE;
1048
0
}