Coverage Report

Created: 2026-10-15 06:31

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