Coverage Report

Created: 2026-02-26 06:54

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