Coverage Report

Created: 2026-01-10 06:35

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
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
  {
305
0
    wStream* s = Stream_New(NULL, REMDESK_CHANNEL_CTL_SIZE + ctlHeader.ch.DataLength);
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
  }
325
0
  if (error)
326
0
    WLog_ERR(TAG, "remdesk_virtual_channel_write failed with error %" PRIu32 "!", error);
327
328
0
out:
329
0
  free(raConnectionStringW);
330
0
  free(expertBlobW);
331
332
0
  return error;
333
0
}
334
335
/**
336
 * Function description
337
 *
338
 * @return 0 on success, otherwise a Win32 error code
339
 */
340
static UINT remdesk_send_ctl_remote_control_desktop_pdu(remdeskPlugin* remdesk)
341
0
{
342
0
  UINT error = 0;
343
0
  size_t length = 0;
344
345
0
  WINPR_ASSERT(remdesk);
346
0
  WINPR_ASSERT(remdesk->rdpcontext);
347
0
  rdpSettings* settings = remdesk->rdpcontext->settings;
348
0
  WINPR_ASSERT(settings);
349
350
0
  const char* raConnectionString =
351
0
      freerdp_settings_get_string(settings, FreeRDP_RemoteAssistanceRCTicket);
352
0
  WCHAR* raConnectionStringW = ConvertUtf8ToWCharAlloc(raConnectionString, &length);
353
0
  size_t cbRaConnectionStringW = length * sizeof(WCHAR);
354
355
0
  if (!raConnectionStringW)
356
0
    return ERROR_INTERNAL_ERROR;
357
358
0
  REMDESK_CTL_HEADER ctlHeader = { 0 };
359
0
  error = remdesk_prepare_ctl_header(&ctlHeader, REMDESK_CTL_REMOTE_CONTROL_DESKTOP,
360
0
                                     cbRaConnectionStringW);
361
0
  if (error != CHANNEL_RC_OK)
362
0
    goto out;
363
364
0
  {
365
0
    wStream* s = Stream_New(NULL, REMDESK_CHANNEL_CTL_SIZE + ctlHeader.ch.DataLength);
366
367
0
    if (!s)
368
0
    {
369
0
      WLog_ERR(TAG, "Stream_New failed!");
370
0
      error = CHANNEL_RC_NO_MEMORY;
371
0
      goto out;
372
0
    }
373
374
0
    error = remdesk_write_ctl_header(s, &ctlHeader);
375
0
    if (error)
376
0
    {
377
0
      Stream_Free(s, TRUE);
378
0
      goto out;
379
0
    }
380
0
    Stream_Write(s, raConnectionStringW, cbRaConnectionStringW);
381
0
    Stream_SealLength(s);
382
383
0
    if ((error = remdesk_virtual_channel_write(remdesk, s)))
384
0
      WLog_ERR(TAG, "remdesk_virtual_channel_write failed with error %" PRIu32 "!", error);
385
0
  }
386
387
0
out:
388
0
  free(raConnectionStringW);
389
390
0
  return error;
391
0
}
392
393
/**
394
 * Function description
395
 *
396
 * @return 0 on success, otherwise a Win32 error code
397
 */
398
static UINT remdesk_send_ctl_verify_password_pdu(remdeskPlugin* remdesk)
399
0
{
400
0
  size_t cbExpertBlobW = 0;
401
0
  REMDESK_CTL_VERIFY_PASSWORD_PDU pdu = { 0 };
402
403
0
  WINPR_ASSERT(remdesk);
404
405
0
  UINT error = remdesk_generate_expert_blob(remdesk);
406
0
  if (error)
407
0
  {
408
0
    WLog_ERR(TAG, "remdesk_generate_expert_blob failed with error %" PRIu32 "!", error);
409
0
    return error;
410
0
  }
411
412
0
  pdu.expertBlob = remdesk->ExpertBlob;
413
0
  WCHAR* expertBlobW = ConvertUtf8ToWCharAlloc(pdu.expertBlob, &cbExpertBlobW);
414
415
0
  if (!expertBlobW)
416
0
    goto out;
417
418
0
  cbExpertBlobW = cbExpertBlobW * sizeof(WCHAR);
419
0
  error =
420
0
      remdesk_prepare_ctl_header(&(pdu.ctlHeader), REMDESK_CTL_VERIFY_PASSWORD, cbExpertBlobW);
421
0
  if (error)
422
0
    goto out;
423
424
0
  {
425
0
    wStream* s =
426
0
        Stream_New(NULL, 1ULL * REMDESK_CHANNEL_CTL_SIZE + pdu.ctlHeader.ch.DataLength);
427
428
0
    if (!s)
429
0
    {
430
0
      WLog_ERR(TAG, "Stream_New failed!");
431
0
      error = CHANNEL_RC_NO_MEMORY;
432
0
      goto out;
433
0
    }
434
435
0
    error = remdesk_write_ctl_header(s, &(pdu.ctlHeader));
436
0
    if (error)
437
0
    {
438
0
      Stream_Free(s, TRUE);
439
0
      goto out;
440
0
    }
441
0
    Stream_Write(s, expertBlobW, cbExpertBlobW);
442
0
    Stream_SealLength(s);
443
444
0
    error = remdesk_virtual_channel_write(remdesk, s);
445
0
  }
446
0
  if (error)
447
0
    WLog_ERR(TAG, "remdesk_virtual_channel_write failed with error %" PRIu32 "!", error);
448
449
0
out:
450
0
  free(expertBlobW);
451
452
0
  return error;
453
0
}
454
455
/**
456
 * Function description
457
 *
458
 * @return 0 on success, otherwise a Win32 error code
459
 */
460
static UINT remdesk_send_ctl_expert_on_vista_pdu(remdeskPlugin* remdesk)
461
0
{
462
0
  REMDESK_CTL_EXPERT_ON_VISTA_PDU pdu = { 0 };
463
464
0
  WINPR_ASSERT(remdesk);
465
466
0
  UINT error = remdesk_generate_expert_blob(remdesk);
467
0
  if (error)
468
0
  {
469
0
    WLog_ERR(TAG, "remdesk_generate_expert_blob failed with error %" PRIu32 "!", error);
470
0
    return error;
471
0
  }
472
0
  if (remdesk->EncryptedPassStubSize > UINT32_MAX)
473
0
    return ERROR_INTERNAL_ERROR;
474
475
0
  pdu.EncryptedPasswordLength = (UINT32)remdesk->EncryptedPassStubSize;
476
0
  pdu.EncryptedPassword = remdesk->EncryptedPassStub;
477
0
  error = remdesk_prepare_ctl_header(&(pdu.ctlHeader), REMDESK_CTL_EXPERT_ON_VISTA,
478
0
                                     pdu.EncryptedPasswordLength);
479
0
  if (error)
480
0
    return error;
481
482
0
  wStream* s = Stream_New(NULL, REMDESK_CHANNEL_CTL_SIZE + pdu.ctlHeader.ch.DataLength);
483
484
0
  if (!s)
485
0
  {
486
0
    WLog_ERR(TAG, "Stream_New failed!");
487
0
    return CHANNEL_RC_NO_MEMORY;
488
0
  }
489
490
0
  error = remdesk_write_ctl_header(s, &(pdu.ctlHeader));
491
0
  if (error)
492
0
  {
493
0
    Stream_Free(s, TRUE);
494
0
    return error;
495
0
  }
496
0
  Stream_Write(s, pdu.EncryptedPassword, pdu.EncryptedPasswordLength);
497
0
  Stream_SealLength(s);
498
0
  return remdesk_virtual_channel_write(remdesk, s);
499
0
}
500
501
/**
502
 * Function description
503
 *
504
 * @return 0 on success, otherwise a Win32 error code
505
 */
506
static UINT remdesk_recv_ctl_pdu(remdeskPlugin* remdesk, wStream* s, REMDESK_CHANNEL_HEADER* header)
507
0
{
508
0
  UINT error = CHANNEL_RC_OK;
509
0
  UINT32 msgType = 0;
510
0
  UINT32 result = 0;
511
512
0
  WINPR_ASSERT(remdesk);
513
0
  WINPR_ASSERT(s);
514
0
  WINPR_ASSERT(header);
515
516
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
517
0
    return ERROR_INVALID_DATA;
518
519
0
  Stream_Read_UINT32(s, msgType); /* msgType (4 bytes) */
520
521
  // WLog_DBG(TAG, "msgType: %"PRIu32"", msgType);
522
523
0
  switch (msgType)
524
0
  {
525
0
    case REMDESK_CTL_REMOTE_CONTROL_DESKTOP:
526
0
      break;
527
528
0
    case REMDESK_CTL_RESULT:
529
0
      if ((error = remdesk_recv_ctl_result_pdu(remdesk, s, header, &result)))
530
0
        WLog_ERR(TAG, "remdesk_recv_ctl_result_pdu failed with error %" PRIu32 "", error);
531
532
0
      break;
533
534
0
    case REMDESK_CTL_AUTHENTICATE:
535
0
      break;
536
537
0
    case REMDESK_CTL_SERVER_ANNOUNCE:
538
0
      if ((error = remdesk_recv_ctl_server_announce_pdu(remdesk, s, header)))
539
0
        WLog_ERR(TAG, "remdesk_recv_ctl_server_announce_pdu failed with error %" PRIu32 "",
540
0
                 error);
541
542
0
      break;
543
544
0
    case REMDESK_CTL_DISCONNECT:
545
0
      break;
546
547
0
    case REMDESK_CTL_VERSIONINFO:
548
0
      if ((error = remdesk_recv_ctl_version_info_pdu(remdesk, s, header)))
549
0
      {
550
0
        WLog_ERR(TAG, "remdesk_recv_ctl_version_info_pdu failed with error %" PRIu32 "",
551
0
                 error);
552
0
        break;
553
0
      }
554
555
0
      if (remdesk->Version == 1)
556
0
      {
557
0
        if ((error = remdesk_send_ctl_version_info_pdu(remdesk)))
558
0
        {
559
0
          WLog_ERR(TAG, "remdesk_send_ctl_version_info_pdu failed with error %" PRIu32 "",
560
0
                   error);
561
0
          break;
562
0
        }
563
564
0
        if ((error = remdesk_send_ctl_authenticate_pdu(remdesk)))
565
0
        {
566
0
          WLog_ERR(TAG, "remdesk_send_ctl_authenticate_pdu failed with error %" PRIu32 "",
567
0
                   error);
568
0
          break;
569
0
        }
570
571
0
        if ((error = remdesk_send_ctl_remote_control_desktop_pdu(remdesk)))
572
0
        {
573
0
          WLog_ERR(
574
0
              TAG,
575
0
              "remdesk_send_ctl_remote_control_desktop_pdu failed with error %" PRIu32 "",
576
0
              error);
577
0
          break;
578
0
        }
579
0
      }
580
0
      else if (remdesk->Version == 2)
581
0
      {
582
0
        if ((error = remdesk_send_ctl_expert_on_vista_pdu(remdesk)))
583
0
        {
584
0
          WLog_ERR(TAG,
585
0
                   "remdesk_send_ctl_expert_on_vista_pdu failed with error %" PRIu32 "",
586
0
                   error);
587
0
          break;
588
0
        }
589
590
0
        if ((error = remdesk_send_ctl_verify_password_pdu(remdesk)))
591
0
        {
592
0
          WLog_ERR(TAG,
593
0
                   "remdesk_send_ctl_verify_password_pdu failed with error %" PRIu32 "",
594
0
                   error);
595
0
          break;
596
0
        }
597
0
      }
598
599
0
      break;
600
601
0
    case REMDESK_CTL_ISCONNECTED:
602
0
      break;
603
604
0
    case REMDESK_CTL_VERIFY_PASSWORD:
605
0
      break;
606
607
0
    case REMDESK_CTL_EXPERT_ON_VISTA:
608
0
      break;
609
610
0
    case REMDESK_CTL_RANOVICE_NAME:
611
0
      break;
612
613
0
    case REMDESK_CTL_RAEXPERT_NAME:
614
0
      break;
615
616
0
    case REMDESK_CTL_TOKEN:
617
0
      break;
618
619
0
    default:
620
0
      WLog_ERR(TAG, "unknown msgType: %" PRIu32 "", msgType);
621
0
      error = ERROR_INVALID_DATA;
622
0
      break;
623
0
  }
624
625
0
  return error;
626
0
}
627
628
/**
629
 * Function description
630
 *
631
 * @return 0 on success, otherwise a Win32 error code
632
 */
633
static UINT remdesk_process_receive(remdeskPlugin* remdesk, wStream* s)
634
0
{
635
0
  UINT status = 0;
636
0
  REMDESK_CHANNEL_HEADER header;
637
638
0
  WINPR_ASSERT(remdesk);
639
0
  WINPR_ASSERT(s);
640
641
0
  if ((status = remdesk_read_channel_header(s, &header)))
642
0
  {
643
0
    WLog_ERR(TAG, "remdesk_read_channel_header failed with error %" PRIu32 "", status);
644
0
    return status;
645
0
  }
646
647
0
  if (strcmp(header.ChannelName, "RC_CTL") == 0)
648
0
  {
649
0
    status = remdesk_recv_ctl_pdu(remdesk, s, &header);
650
0
  }
651
0
  else if (strcmp(header.ChannelName, "70") == 0)
652
0
  {
653
0
  }
654
0
  else if (strcmp(header.ChannelName, "71") == 0)
655
0
  {
656
0
  }
657
0
  else if (strcmp(header.ChannelName, ".") == 0)
658
0
  {
659
0
  }
660
0
  else if (strcmp(header.ChannelName, "1000.") == 0)
661
0
  {
662
0
  }
663
0
  else if (strcmp(header.ChannelName, "RA_FX") == 0)
664
0
  {
665
0
  }
666
0
  else
667
0
  {
668
0
  }
669
670
0
  return status;
671
0
}
672
673
static void remdesk_process_connect(WINPR_ATTR_UNUSED remdeskPlugin* remdesk)
674
0
{
675
0
  WINPR_ASSERT(remdesk);
676
0
  WLog_ERR("TODO", "TODO: implement");
677
0
}
678
679
/**
680
 * Function description
681
 *
682
 * @return 0 on success, otherwise a Win32 error code
683
 */
684
static UINT remdesk_virtual_channel_event_data_received(remdeskPlugin* remdesk, const void* pData,
685
                                                        UINT32 dataLength, UINT32 totalLength,
686
                                                        UINT32 dataFlags)
687
0
{
688
0
  wStream* data_in = NULL;
689
690
0
  WINPR_ASSERT(remdesk);
691
692
0
  if ((dataFlags & CHANNEL_FLAG_SUSPEND) || (dataFlags & CHANNEL_FLAG_RESUME))
693
0
  {
694
0
    return CHANNEL_RC_OK;
695
0
  }
696
697
0
  if (dataFlags & CHANNEL_FLAG_FIRST)
698
0
  {
699
0
    if (remdesk->data_in)
700
0
      Stream_Free(remdesk->data_in, TRUE);
701
702
0
    remdesk->data_in = Stream_New(NULL, totalLength);
703
704
0
    if (!remdesk->data_in)
705
0
    {
706
0
      WLog_ERR(TAG, "Stream_New failed!");
707
0
      return CHANNEL_RC_NO_MEMORY;
708
0
    }
709
0
  }
710
711
0
  data_in = remdesk->data_in;
712
713
0
  if (!Stream_EnsureRemainingCapacity(data_in, dataLength))
714
0
  {
715
0
    WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
716
0
    return CHANNEL_RC_NO_MEMORY;
717
0
  }
718
719
0
  Stream_Write(data_in, pData, dataLength);
720
721
0
  if (dataFlags & CHANNEL_FLAG_LAST)
722
0
  {
723
0
    if (Stream_Capacity(data_in) != Stream_GetPosition(data_in))
724
0
    {
725
0
      WLog_ERR(TAG, "read error");
726
0
      return ERROR_INTERNAL_ERROR;
727
0
    }
728
729
0
    remdesk->data_in = NULL;
730
0
    Stream_SealLength(data_in);
731
0
    Stream_SetPosition(data_in, 0);
732
733
0
    if (!MessageQueue_Post(remdesk->queue, NULL, 0, (void*)data_in, NULL))
734
0
    {
735
0
      WLog_ERR(TAG, "MessageQueue_Post failed!");
736
0
      return ERROR_INTERNAL_ERROR;
737
0
    }
738
0
  }
739
740
0
  return CHANNEL_RC_OK;
741
0
}
742
743
static VOID VCAPITYPE remdesk_virtual_channel_open_event_ex(LPVOID lpUserParam, DWORD openHandle,
744
                                                            UINT event, LPVOID pData,
745
                                                            UINT32 dataLength, UINT32 totalLength,
746
                                                            UINT32 dataFlags)
747
0
{
748
0
  UINT error = CHANNEL_RC_OK;
749
0
  remdeskPlugin* remdesk = (remdeskPlugin*)lpUserParam;
750
751
0
  switch (event)
752
0
  {
753
0
    case CHANNEL_EVENT_INITIALIZED:
754
0
      break;
755
756
0
    case CHANNEL_EVENT_DATA_RECEIVED:
757
0
      if (!remdesk || (remdesk->OpenHandle != openHandle))
758
0
      {
759
0
        WLog_ERR(TAG, "error no match");
760
0
        return;
761
0
      }
762
0
      if ((error = remdesk_virtual_channel_event_data_received(remdesk, pData, dataLength,
763
0
                                                               totalLength, dataFlags)))
764
0
        WLog_ERR(TAG,
765
0
                 "remdesk_virtual_channel_event_data_received failed with error %" PRIu32
766
0
                 "!",
767
0
                 error);
768
769
0
      break;
770
771
0
    case CHANNEL_EVENT_WRITE_CANCELLED:
772
0
    case CHANNEL_EVENT_WRITE_COMPLETE:
773
0
    {
774
0
      wStream* s = (wStream*)pData;
775
0
      Stream_Free(s, TRUE);
776
0
    }
777
0
    break;
778
779
0
    case CHANNEL_EVENT_USER:
780
0
      break;
781
782
0
    default:
783
0
      WLog_ERR(TAG, "unhandled event %" PRIu32 "!", event);
784
0
      error = ERROR_INTERNAL_ERROR;
785
0
      break;
786
0
  }
787
788
0
  if (error && remdesk && remdesk->rdpcontext)
789
0
    setChannelError(remdesk->rdpcontext, error,
790
0
                    "remdesk_virtual_channel_open_event_ex reported an error");
791
0
}
792
793
static DWORD WINAPI remdesk_virtual_channel_client_thread(LPVOID arg)
794
0
{
795
0
  wStream* data = NULL;
796
0
  wMessage message = { 0 };
797
0
  remdeskPlugin* remdesk = (remdeskPlugin*)arg;
798
0
  UINT error = CHANNEL_RC_OK;
799
800
0
  WINPR_ASSERT(remdesk);
801
802
0
  remdesk_process_connect(remdesk);
803
804
0
  while (1)
805
0
  {
806
0
    if (!MessageQueue_Wait(remdesk->queue))
807
0
    {
808
0
      WLog_ERR(TAG, "MessageQueue_Wait failed!");
809
0
      error = ERROR_INTERNAL_ERROR;
810
0
      break;
811
0
    }
812
813
0
    if (!MessageQueue_Peek(remdesk->queue, &message, TRUE))
814
0
    {
815
0
      WLog_ERR(TAG, "MessageQueue_Peek failed!");
816
0
      error = ERROR_INTERNAL_ERROR;
817
0
      break;
818
0
    }
819
820
0
    if (message.id == WMQ_QUIT)
821
0
      break;
822
823
0
    if (message.id == 0)
824
0
    {
825
0
      data = (wStream*)message.wParam;
826
827
0
      if ((error = remdesk_process_receive(remdesk, data)))
828
0
      {
829
0
        WLog_ERR(TAG, "remdesk_process_receive failed with error %" PRIu32 "!", error);
830
0
        Stream_Free(data, TRUE);
831
0
        break;
832
0
      }
833
834
0
      Stream_Free(data, TRUE);
835
0
    }
836
0
  }
837
838
0
  if (error && remdesk->rdpcontext)
839
0
    setChannelError(remdesk->rdpcontext, error,
840
0
                    "remdesk_virtual_channel_client_thread reported an error");
841
842
0
  ExitThread(error);
843
0
  return error;
844
0
}
845
846
/**
847
 * Function description
848
 *
849
 * @return 0 on success, otherwise a Win32 error code
850
 */
851
static UINT remdesk_virtual_channel_event_connected(remdeskPlugin* remdesk,
852
                                                    WINPR_ATTR_UNUSED LPVOID pData,
853
                                                    WINPR_ATTR_UNUSED UINT32 dataLength)
854
0
{
855
0
  UINT error = 0;
856
857
0
  WINPR_ASSERT(remdesk);
858
859
0
  remdesk->queue = MessageQueue_New(NULL);
860
861
0
  if (!remdesk->queue)
862
0
  {
863
0
    WLog_ERR(TAG, "MessageQueue_New failed!");
864
0
    error = CHANNEL_RC_NO_MEMORY;
865
0
    goto error_out;
866
0
  }
867
868
0
  remdesk->thread =
869
0
      CreateThread(NULL, 0, remdesk_virtual_channel_client_thread, (void*)remdesk, 0, NULL);
870
871
0
  if (!remdesk->thread)
872
0
  {
873
0
    WLog_ERR(TAG, "CreateThread failed");
874
0
    error = ERROR_INTERNAL_ERROR;
875
0
    goto error_out;
876
0
  }
877
878
0
  return remdesk->channelEntryPoints.pVirtualChannelOpenEx(
879
0
      remdesk->InitHandle, &remdesk->OpenHandle, remdesk->channelDef.name,
880
0
      remdesk_virtual_channel_open_event_ex);
881
0
error_out:
882
0
  MessageQueue_Free(remdesk->queue);
883
0
  remdesk->queue = NULL;
884
0
  return error;
885
0
}
886
887
/**
888
 * Function description
889
 *
890
 * @return 0 on success, otherwise a Win32 error code
891
 */
892
static UINT remdesk_virtual_channel_event_disconnected(remdeskPlugin* remdesk)
893
0
{
894
0
  UINT rc = CHANNEL_RC_OK;
895
896
0
  WINPR_ASSERT(remdesk);
897
898
0
  if (remdesk->queue && remdesk->thread)
899
0
  {
900
0
    if (MessageQueue_PostQuit(remdesk->queue, 0) &&
901
0
        (WaitForSingleObject(remdesk->thread, INFINITE) == WAIT_FAILED))
902
0
    {
903
0
      rc = GetLastError();
904
0
      WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", rc);
905
0
      return rc;
906
0
    }
907
0
  }
908
909
0
  if (remdesk->OpenHandle != 0)
910
0
  {
911
0
    WINPR_ASSERT(remdesk->channelEntryPoints.pVirtualChannelCloseEx);
912
0
    rc = remdesk->channelEntryPoints.pVirtualChannelCloseEx(remdesk->InitHandle,
913
0
                                                            remdesk->OpenHandle);
914
915
0
    if (CHANNEL_RC_OK != rc)
916
0
    {
917
0
      WLog_ERR(TAG, "pVirtualChannelCloseEx failed with %s [%08" PRIX32 "]",
918
0
               WTSErrorToString(rc), rc);
919
0
    }
920
921
0
    remdesk->OpenHandle = 0;
922
0
  }
923
0
  MessageQueue_Free(remdesk->queue);
924
0
  (void)CloseHandle(remdesk->thread);
925
0
  Stream_Free(remdesk->data_in, TRUE);
926
0
  remdesk->data_in = NULL;
927
0
  remdesk->queue = NULL;
928
0
  remdesk->thread = NULL;
929
0
  return rc;
930
0
}
931
932
static void remdesk_virtual_channel_event_terminated(remdeskPlugin* remdesk)
933
0
{
934
0
  WINPR_ASSERT(remdesk);
935
936
0
  remdesk->InitHandle = 0;
937
0
  free(remdesk->context);
938
0
  free(remdesk);
939
0
}
940
941
static VOID VCAPITYPE remdesk_virtual_channel_init_event_ex(LPVOID lpUserParam, LPVOID pInitHandle,
942
                                                            UINT event, LPVOID pData,
943
                                                            UINT dataLength)
944
0
{
945
0
  UINT error = CHANNEL_RC_OK;
946
0
  remdeskPlugin* remdesk = (remdeskPlugin*)lpUserParam;
947
948
0
  if (!remdesk || (remdesk->InitHandle != pInitHandle))
949
0
  {
950
0
    WLog_ERR(TAG, "error no match");
951
0
    return;
952
0
  }
953
954
0
  switch (event)
955
0
  {
956
0
    case CHANNEL_EVENT_CONNECTED:
957
0
      if ((error = remdesk_virtual_channel_event_connected(remdesk, pData, dataLength)))
958
0
        WLog_ERR(TAG,
959
0
                 "remdesk_virtual_channel_event_connected failed with error %" PRIu32 "",
960
0
                 error);
961
962
0
      break;
963
964
0
    case CHANNEL_EVENT_DISCONNECTED:
965
0
      if ((error = remdesk_virtual_channel_event_disconnected(remdesk)))
966
0
        WLog_ERR(TAG,
967
0
                 "remdesk_virtual_channel_event_disconnected failed with error %" PRIu32 "",
968
0
                 error);
969
970
0
      break;
971
972
0
    case CHANNEL_EVENT_TERMINATED:
973
0
      remdesk_virtual_channel_event_terminated(remdesk);
974
0
      break;
975
976
0
    case CHANNEL_EVENT_ATTACHED:
977
0
    case CHANNEL_EVENT_DETACHED:
978
0
    default:
979
0
      break;
980
0
  }
981
982
0
  if (error && remdesk->rdpcontext)
983
0
    setChannelError(remdesk->rdpcontext, error,
984
0
                    "remdesk_virtual_channel_init_event reported an error");
985
0
}
986
987
/* remdesk is always built-in */
988
#define VirtualChannelEntryEx remdesk_VirtualChannelEntryEx
989
990
FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints,
991
                                                         PVOID pInitHandle))
992
0
{
993
0
  UINT rc = 0;
994
0
  remdeskPlugin* remdesk = NULL;
995
0
  RemdeskClientContext* context = NULL;
996
0
  CHANNEL_ENTRY_POINTS_FREERDP_EX* pEntryPointsEx = NULL;
997
998
0
  if (!pEntryPoints)
999
0
  {
1000
0
    return FALSE;
1001
0
  }
1002
1003
0
  remdesk = (remdeskPlugin*)calloc(1, sizeof(remdeskPlugin));
1004
1005
0
  if (!remdesk)
1006
0
  {
1007
0
    WLog_ERR(TAG, "calloc failed!");
1008
0
    return FALSE;
1009
0
  }
1010
1011
0
  remdesk->channelDef.options = CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP |
1012
0
                                CHANNEL_OPTION_COMPRESS_RDP | CHANNEL_OPTION_SHOW_PROTOCOL;
1013
0
  (void)sprintf_s(remdesk->channelDef.name, ARRAYSIZE(remdesk->channelDef.name),
1014
0
                  REMDESK_SVC_CHANNEL_NAME);
1015
0
  remdesk->Version = 2;
1016
0
  pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
1017
1018
0
  if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX)) &&
1019
0
      (pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER))
1020
0
  {
1021
0
    context = (RemdeskClientContext*)calloc(1, sizeof(RemdeskClientContext));
1022
1023
0
    if (!context)
1024
0
    {
1025
0
      WLog_ERR(TAG, "calloc failed!");
1026
0
      goto error_out;
1027
0
    }
1028
1029
0
    context->handle = (void*)remdesk;
1030
0
    remdesk->context = context;
1031
0
    remdesk->rdpcontext = pEntryPointsEx->context;
1032
0
  }
1033
1034
0
  CopyMemory(&(remdesk->channelEntryPoints), pEntryPoints,
1035
0
             sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX));
1036
0
  remdesk->InitHandle = pInitHandle;
1037
0
  rc = remdesk->channelEntryPoints.pVirtualChannelInitEx(
1038
0
      remdesk, context, pInitHandle, &remdesk->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000,
1039
0
      remdesk_virtual_channel_init_event_ex);
1040
1041
0
  if (CHANNEL_RC_OK != rc)
1042
0
  {
1043
0
    WLog_ERR(TAG, "pVirtualChannelInitEx failed with %s [%08" PRIX32 "]", WTSErrorToString(rc),
1044
0
             rc);
1045
0
    goto error_out;
1046
0
  }
1047
1048
0
  remdesk->channelEntryPoints.pInterface = context;
1049
0
  return TRUE;
1050
0
error_out:
1051
0
  free(remdesk);
1052
0
  free(context);
1053
  return FALSE;
1054
0
}