Coverage Report

Created: 2026-01-09 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/libfreerdp/core/activation.c
Line
Count
Source
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Activation Sequence
4
 *
5
 * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
20
#include <freerdp/config.h>
21
22
#include "settings.h"
23
24
#include <winpr/assert.h>
25
#include <winpr/cast.h>
26
27
#include "activation.h"
28
#include "display.h"
29
30
34.5k
#define TAG FREERDP_TAG("core.activation")
31
32
static BOOL rdp_recv_client_font_list_pdu(wStream* s);
33
static BOOL rdp_recv_client_persistent_key_list_pdu(wStream* s);
34
static BOOL rdp_send_server_font_map_pdu(rdpRdp* rdp);
35
36
static BOOL rdp_write_synchronize_pdu(wStream* s, const rdpSettings* settings)
37
0
{
38
0
  const UINT32 PduSource = freerdp_settings_get_uint32(settings, FreeRDP_PduSource);
39
40
0
  if (!Stream_CheckAndLogRequiredCapacity(TAG, (s), 4))
41
0
    return FALSE;
42
0
  Stream_Write_UINT16(s, SYNCMSGTYPE_SYNC); /* messageType (2 bytes) */
43
0
  Stream_Write_UINT16(s,
44
0
                      WINPR_ASSERTING_INT_CAST(uint16_t, PduSource)); /* targetUser (2 bytes) */
45
0
  return TRUE;
46
0
}
47
48
static BOOL rdp_recv_sync_pdu(rdpRdp* rdp, wStream* s, const char* what)
49
34.5k
{
50
34.5k
  UINT16 msgType = 0;
51
34.5k
  UINT16 targetUser = 0;
52
53
34.5k
  WINPR_UNUSED(rdp);
54
34.5k
  if (!Stream_CheckAndLogRequiredLengthEx(TAG, WLOG_WARN, s, 4, 1, "%s(%s:%" PRIuz ") %s",
55
34.5k
                                          __func__, __FILE__, (size_t)__LINE__, what))
56
28.6k
    return FALSE;
57
5.96k
  Stream_Read_UINT16(s, msgType);
58
5.96k
  if (msgType != SYNCMSGTYPE_SYNC)
59
5.84k
  {
60
5.84k
    WLog_WARN(TAG, "%s: Invalid messageType=0x%04" PRIx16 ", expected 0x%04" PRIx16, what,
61
5.84k
              msgType, SYNCMSGTYPE_SYNC);
62
5.84k
    return FALSE;
63
5.84k
  }
64
120
  Stream_Read_UINT16(s, targetUser);
65
120
  WLog_VRB(TAG, "%s: targetUser=0x%04" PRIx16, what, targetUser);
66
120
  return TRUE;
67
5.96k
}
68
69
BOOL rdp_recv_server_synchronize_pdu(rdpRdp* rdp, wStream* s)
70
17.2k
{
71
17.2k
  if (!rdp_recv_sync_pdu(rdp, s, "[MS-RDPBCGR] 2.2.1.19 Server Synchronize PDU"))
72
17.2k
    return FALSE;
73
57
  return rdp_finalize_set_flag(rdp, FINALIZE_SC_SYNCHRONIZE_PDU);
74
17.2k
}
75
76
BOOL rdp_send_server_synchronize_pdu(rdpRdp* rdp)
77
0
{
78
0
  UINT16 sec_flags = 0;
79
0
  wStream* s = rdp_data_pdu_init(rdp, &sec_flags);
80
0
  if (!s)
81
0
    return FALSE;
82
83
0
  WINPR_ASSERT(rdp);
84
0
  if (!rdp_write_synchronize_pdu(s, rdp->settings))
85
0
  {
86
0
    Stream_Free(s, TRUE);
87
0
    return FALSE;
88
0
  }
89
90
0
  WINPR_ASSERT(rdp->mcs);
91
0
  return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_SYNCHRONIZE, rdp->mcs->userId, sec_flags);
92
0
}
93
94
BOOL rdp_recv_client_synchronize_pdu(rdpRdp* rdp, wStream* s)
95
17.2k
{
96
17.2k
  if (!rdp_recv_sync_pdu(rdp, s, "[MS-RDPBCGR] 2.2.1.14 Client Synchronize PDU"))
97
17.2k
    return FALSE;
98
63
  return rdp_finalize_set_flag(rdp, FINALIZE_CS_SYNCHRONIZE_PDU);
99
17.2k
}
100
101
BOOL rdp_send_client_synchronize_pdu(rdpRdp* rdp)
102
0
{
103
0
  UINT16 sec_flags = 0;
104
0
  wStream* s = rdp_data_pdu_init(rdp, &sec_flags);
105
0
  if (!s)
106
0
    return FALSE;
107
108
0
  WINPR_ASSERT(rdp);
109
0
  if (!rdp_write_synchronize_pdu(s, rdp->settings))
110
0
  {
111
0
    Stream_Free(s, TRUE);
112
0
    return FALSE;
113
0
  }
114
115
0
  WINPR_ASSERT(rdp->mcs);
116
0
  return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_SYNCHRONIZE, rdp->mcs->userId, sec_flags);
117
0
}
118
119
static BOOL rdp_recv_control_pdu(wStream* s, UINT16* action, UINT16* grantId, UINT32* controlId)
120
54
{
121
54
  WINPR_ASSERT(s);
122
54
  WINPR_ASSERT(action);
123
54
  WINPR_ASSERT(grantId);
124
54
  WINPR_ASSERT(controlId);
125
126
54
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
127
4
    return FALSE;
128
129
50
  Stream_Read_UINT16(s, *action);    /* action (2 bytes) */
130
50
  Stream_Read_UINT16(s, *grantId);   /* grantId (2 bytes) */
131
50
  Stream_Read_UINT32(s, *controlId); /* controlId (4 bytes) */
132
50
  return TRUE;
133
54
}
134
135
static BOOL rdp_write_client_control_pdu(wStream* s, UINT16 action, UINT16 grantId,
136
                                         UINT32 controlId)
137
0
{
138
0
  WINPR_ASSERT(s);
139
0
  if (!Stream_CheckAndLogRequiredCapacity(TAG, (s), 8))
140
0
    return FALSE;
141
0
  Stream_Write_UINT16(s, action);    /* action (2 bytes) */
142
0
  Stream_Write_UINT16(s, grantId);   /* grantId (2 bytes) */
143
0
  Stream_Write_UINT32(s, controlId); /* controlId (4 bytes) */
144
0
  return TRUE;
145
0
}
146
147
BOOL rdp_recv_server_control_pdu(rdpRdp* rdp, wStream* s)
148
54
{
149
54
  UINT16 action = 0;
150
54
  UINT16 grantId = 0;
151
54
  UINT32 controlId = 0;
152
153
54
  WINPR_ASSERT(rdp);
154
54
  WINPR_ASSERT(s);
155
156
54
  if (!rdp_recv_control_pdu(s, &action, &grantId, &controlId))
157
4
    return FALSE;
158
159
50
  switch (action)
160
50
  {
161
4
    case CTRLACTION_COOPERATE:
162
4
      return rdp_finalize_set_flag(rdp, FINALIZE_SC_CONTROL_COOPERATE_PDU);
163
164
1
    case CTRLACTION_GRANTED_CONTROL:
165
1
      rdp->resendFocus = TRUE;
166
1
      return rdp_finalize_set_flag(rdp, FINALIZE_SC_CONTROL_GRANTED_PDU);
167
45
    default:
168
45
    {
169
45
      char buffer[128] = { 0 };
170
45
      WLog_WARN(TAG, "Unexpected control PDU %s",
171
45
                rdp_ctrlaction_string(action, buffer, sizeof(buffer)));
172
173
45
      return FALSE;
174
0
    }
175
50
  }
176
50
}
177
178
BOOL rdp_send_server_control_cooperate_pdu(rdpRdp* rdp)
179
0
{
180
0
  UINT16 sec_flags = 0;
181
0
  wStream* s = rdp_data_pdu_init(rdp, &sec_flags);
182
0
  if (!s)
183
0
    return FALSE;
184
0
  if (!Stream_CheckAndLogRequiredCapacity(TAG, (s), 8))
185
0
  {
186
0
    Stream_Free(s, TRUE);
187
0
    return FALSE;
188
0
  }
189
0
  Stream_Write_UINT16(s, CTRLACTION_COOPERATE); /* action (2 bytes) */
190
0
  Stream_Write_UINT16(s, 0);                    /* grantId (2 bytes) */
191
0
  Stream_Write_UINT32(s, 0);                    /* controlId (4 bytes) */
192
193
0
  WINPR_ASSERT(rdp->mcs);
194
0
  return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_CONTROL, rdp->mcs->userId, sec_flags);
195
0
}
196
197
BOOL rdp_send_server_control_granted_pdu(rdpRdp* rdp)
198
0
{
199
0
  UINT16 sec_flags = 0;
200
0
  wStream* s = rdp_data_pdu_init(rdp, &sec_flags);
201
0
  if (!s)
202
0
    return FALSE;
203
0
  if (!Stream_CheckAndLogRequiredCapacity(TAG, (s), 8))
204
0
  {
205
0
    Stream_Free(s, TRUE);
206
0
    return FALSE;
207
0
  }
208
209
0
  WINPR_ASSERT(rdp->mcs);
210
0
  Stream_Write_UINT16(s, CTRLACTION_GRANTED_CONTROL); /* action (2 bytes) */
211
0
  Stream_Write_UINT16(s, rdp->mcs->userId);           /* grantId (2 bytes) */
212
0
  Stream_Write_UINT32(s, 0x03EA);                     /* controlId (4 bytes) */
213
0
  return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_CONTROL, rdp->mcs->userId, sec_flags);
214
0
}
215
216
BOOL rdp_send_client_control_pdu(rdpRdp* rdp, UINT16 action)
217
0
{
218
0
  UINT16 GrantId = 0;
219
0
  UINT16 ControlId = 0;
220
221
0
  switch (action)
222
0
  {
223
0
    case CTRLACTION_COOPERATE:
224
0
    case CTRLACTION_REQUEST_CONTROL:
225
0
      break;
226
0
    default:
227
0
      WLog_WARN(TAG,
228
0
                "Invalid client control PDU::action 0x%04" PRIx16 ", not allowed by client",
229
0
                action);
230
0
      return FALSE;
231
0
  }
232
233
0
  UINT16 sec_flags = 0;
234
0
  wStream* s = rdp_data_pdu_init(rdp, &sec_flags);
235
0
  if (!s)
236
0
    return FALSE;
237
0
  if (!rdp_write_client_control_pdu(s, action, GrantId, ControlId))
238
0
  {
239
0
    Stream_Free(s, TRUE);
240
0
    return FALSE;
241
0
  }
242
243
0
  WINPR_ASSERT(rdp->mcs);
244
0
  return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_CONTROL, rdp->mcs->userId, sec_flags);
245
0
}
246
247
static BOOL rdp_write_client_persistent_key_list_pdu(wStream* s,
248
                                                     const RDP_BITMAP_PERSISTENT_INFO* info)
249
0
{
250
0
  WINPR_ASSERT(s);
251
0
  WINPR_ASSERT(info);
252
253
0
  if (!Stream_EnsureRemainingCapacity(s, 24))
254
0
    return FALSE;
255
256
0
  Stream_Write_UINT16(s, info->numEntriesCache0);              /* numEntriesCache0 (2 bytes) */
257
0
  Stream_Write_UINT16(s, info->numEntriesCache1);              /* numEntriesCache1 (2 bytes) */
258
0
  Stream_Write_UINT16(s, info->numEntriesCache2);              /* numEntriesCache2 (2 bytes) */
259
0
  Stream_Write_UINT16(s, info->numEntriesCache3);              /* numEntriesCache3 (2 bytes) */
260
0
  Stream_Write_UINT16(s, info->numEntriesCache4);              /* numEntriesCache4 (2 bytes) */
261
0
  Stream_Write_UINT16(s, info->totalEntriesCache0);            /* totalEntriesCache0 (2 bytes) */
262
0
  Stream_Write_UINT16(s, info->totalEntriesCache1);            /* totalEntriesCache1 (2 bytes) */
263
0
  Stream_Write_UINT16(s, info->totalEntriesCache2);            /* totalEntriesCache2 (2 bytes) */
264
0
  Stream_Write_UINT16(s, info->totalEntriesCache3);            /* totalEntriesCache3 (2 bytes) */
265
0
  Stream_Write_UINT16(s, info->totalEntriesCache4);            /* totalEntriesCache4 (2 bytes) */
266
0
  Stream_Write_UINT8(s, PERSIST_FIRST_PDU | PERSIST_LAST_PDU); /* bBitMask (1 byte) */
267
0
  Stream_Write_UINT8(s, 0);                                    /* pad1 (1 byte) */
268
0
  Stream_Write_UINT16(s, 0);                                   /* pad3 (2 bytes) */
269
                                                               /* entries */
270
271
0
  if (!Stream_EnsureRemainingCapacity(s, info->keyCount * 8ull))
272
0
    return FALSE;
273
274
0
  for (UINT32 index = 0; index < info->keyCount; index++)
275
0
  {
276
0
    const UINT64 val = info->keyList[index];
277
0
    Stream_Write_UINT64(s, val);
278
0
  }
279
280
0
  return TRUE;
281
0
}
282
283
static UINT16 rdp_load_persistent_key_list(rdpRdp* rdp, UINT64** pKeyList)
284
0
{
285
0
  UINT16 keyCount = 0;
286
0
  UINT64* keyList = NULL;
287
0
  rdpPersistentCache* persistent = NULL;
288
0
  rdpSettings* settings = rdp->settings;
289
290
0
  *pKeyList = NULL;
291
292
0
  if (!freerdp_settings_get_bool(settings, FreeRDP_BitmapCachePersistEnabled))
293
0
    return 0;
294
295
0
  if (!settings->BitmapCachePersistFile)
296
0
    return 0;
297
298
0
  persistent = persistent_cache_new();
299
300
0
  if (!persistent)
301
0
    return 0;
302
303
0
  const int status =
304
0
      persistent_cache_open(persistent, settings->BitmapCachePersistFile, FALSE, 0);
305
306
0
  if (status < 1)
307
0
    goto error;
308
309
0
  {
310
0
    const int count = persistent_cache_get_count(persistent);
311
0
    if ((count < 0) || (count > UINT16_MAX))
312
0
      goto error;
313
314
0
    keyCount = (UINT16)count;
315
0
    keyList = (UINT64*)calloc(keyCount, sizeof(UINT64));
316
317
0
    if (!keyList)
318
0
      goto error;
319
320
0
    for (int index = 0; index < count; index++)
321
0
    {
322
0
      PERSISTENT_CACHE_ENTRY cacheEntry = { 0 };
323
324
0
      if (persistent_cache_read_entry(persistent, &cacheEntry) < 1)
325
0
        continue;
326
327
0
      keyList[index] = cacheEntry.key64;
328
0
    }
329
0
  }
330
331
0
  *pKeyList = keyList;
332
333
0
  persistent_cache_free(persistent);
334
0
  return keyCount;
335
0
error:
336
0
  persistent_cache_free(persistent);
337
0
  free(keyList);
338
0
  return 0;
339
0
}
340
341
BOOL rdp_send_client_persistent_key_list_pdu(rdpRdp* rdp)
342
0
{
343
0
  UINT16 keyMaxFrag = 2042;
344
0
  UINT64* keyList = NULL;
345
0
  RDP_BITMAP_PERSISTENT_INFO info = { 0 };
346
0
  WINPR_ASSERT(rdp);
347
0
  rdpSettings* settings = rdp->settings;
348
0
  UINT16 keyCount = rdp_load_persistent_key_list(rdp, &keyList);
349
350
0
  WLog_DBG(TAG, "Persistent Key List: TotalKeyCount: %" PRIu16 " MaxKeyFrag: %" PRIu16, keyCount,
351
0
           keyMaxFrag);
352
353
  // MS-RDPBCGR recommends sending no more than 169 entries at once.
354
  // In practice, sending more than 2042 entries at once triggers an error.
355
  // It should be possible to advertise the entire client bitmap cache
356
  // by sending multiple persistent key list PDUs, but the current code
357
  // only bothers sending a single, smaller list of entries instead.
358
359
0
  if (keyCount > keyMaxFrag)
360
0
    keyCount = keyMaxFrag;
361
362
0
  WINPR_ASSERT(settings->BitmapCacheV2CellInfo[0].numEntries <= UINT16_MAX);
363
0
  info.totalEntriesCache0 = (UINT16)settings->BitmapCacheV2CellInfo[0].numEntries;
364
365
0
  WINPR_ASSERT(settings->BitmapCacheV2CellInfo[1].numEntries <= UINT16_MAX);
366
0
  info.totalEntriesCache1 = (UINT16)settings->BitmapCacheV2CellInfo[1].numEntries;
367
368
0
  WINPR_ASSERT(settings->BitmapCacheV2CellInfo[2].numEntries <= UINT16_MAX);
369
0
  info.totalEntriesCache2 = (UINT16)settings->BitmapCacheV2CellInfo[2].numEntries;
370
371
0
  WINPR_ASSERT(settings->BitmapCacheV2CellInfo[3].numEntries <= UINT16_MAX);
372
0
  info.totalEntriesCache3 = (UINT16)settings->BitmapCacheV2CellInfo[3].numEntries;
373
374
0
  WINPR_ASSERT(settings->BitmapCacheV2CellInfo[4].numEntries <= UINT16_MAX);
375
0
  info.totalEntriesCache4 = (UINT16)settings->BitmapCacheV2CellInfo[4].numEntries;
376
377
0
  info.numEntriesCache0 = MIN(keyCount, info.totalEntriesCache0);
378
0
  keyCount -= info.numEntriesCache0;
379
0
  info.numEntriesCache1 = MIN(keyCount, info.totalEntriesCache1);
380
0
  keyCount -= info.numEntriesCache1;
381
0
  info.numEntriesCache2 = MIN(keyCount, info.totalEntriesCache2);
382
0
  keyCount -= info.numEntriesCache2;
383
0
  info.numEntriesCache3 = MIN(keyCount, info.totalEntriesCache3);
384
0
  keyCount -= info.numEntriesCache3;
385
0
  info.numEntriesCache4 = MIN(keyCount, info.totalEntriesCache4);
386
387
0
  info.totalEntriesCache0 = info.numEntriesCache0;
388
0
  info.totalEntriesCache1 = info.numEntriesCache1;
389
0
  info.totalEntriesCache2 = info.numEntriesCache2;
390
0
  info.totalEntriesCache3 = info.numEntriesCache3;
391
0
  info.totalEntriesCache4 = info.numEntriesCache4;
392
393
0
  keyCount = info.totalEntriesCache0 + info.totalEntriesCache1 + info.totalEntriesCache2 +
394
0
             info.totalEntriesCache3 + info.totalEntriesCache4;
395
396
0
  info.keyCount = keyCount;
397
0
  info.keyList = keyList;
398
399
0
  WLog_DBG(TAG, "persistentKeyList count: %" PRIu32, info.keyCount);
400
401
0
  WLog_DBG(TAG,
402
0
           "numEntriesCache: [0]: %" PRIu16 " [1]: %" PRIu16 " [2]: %" PRIu16 " [3]: %" PRIu16
403
0
           " [4]: %" PRIu16,
404
0
           info.numEntriesCache0, info.numEntriesCache1, info.numEntriesCache2,
405
0
           info.numEntriesCache3, info.numEntriesCache4);
406
407
0
  WLog_DBG(TAG,
408
0
           "totalEntriesCache: [0]: %" PRIu16 " [1]: %" PRIu16 " [2]: %" PRIu16 " [3]: %" PRIu16
409
0
           " [4]: %" PRIu16,
410
0
           info.totalEntriesCache0, info.totalEntriesCache1, info.totalEntriesCache2,
411
0
           info.totalEntriesCache3, info.totalEntriesCache4);
412
413
0
  UINT16 sec_flags = 0;
414
0
  wStream* s = rdp_data_pdu_init(rdp, &sec_flags);
415
416
0
  if (!s)
417
0
  {
418
0
    free(keyList);
419
0
    return FALSE;
420
0
  }
421
422
0
  if (!rdp_write_client_persistent_key_list_pdu(s, &info))
423
0
  {
424
0
    Stream_Free(s, TRUE);
425
0
    free(keyList);
426
0
    return FALSE;
427
0
  }
428
429
0
  WINPR_ASSERT(rdp->mcs);
430
0
  free(keyList);
431
432
0
  return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_BITMAP_CACHE_PERSISTENT_LIST, rdp->mcs->userId,
433
0
                           sec_flags);
434
0
}
435
436
BOOL rdp_recv_client_font_list_pdu(wStream* s)
437
0
{
438
0
  WINPR_ASSERT(s);
439
  /* 2.2.1.18 Client Font List PDU */
440
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
441
0
    return FALSE;
442
443
0
  return Stream_SafeSeek(s, 8);
444
0
}
445
446
BOOL rdp_recv_client_persistent_key_list_pdu(wStream* s)
447
0
{
448
0
  BYTE flags = 0;
449
0
  size_t count = 0;
450
0
  size_t total = 0;
451
0
  UINT16 cache = 0;
452
453
0
  WINPR_ASSERT(s);
454
455
  /* 2.2.1.17.1 Persistent Key List PDU Data (TS_BITMAPCACHE_PERSISTENT_LIST_PDU) */
456
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 21))
457
0
  {
458
0
    WLog_ERR(TAG, "short TS_BITMAPCACHE_PERSISTENT_LIST_PDU, need 21 bytes, got %" PRIuz,
459
0
             Stream_GetRemainingLength(s));
460
0
    return FALSE;
461
0
  }
462
  /* Read numEntriesCacheX for variable length data in PDU */
463
0
  for (size_t x = 0; x < 5; x++)
464
0
  {
465
0
    Stream_Read_UINT16(s, cache);
466
0
    count += cache;
467
0
  }
468
469
  /* Skip totalEntriesCacheX */
470
0
  for (size_t x = 0; x < 5; x++)
471
0
  {
472
0
    UINT16 tmp = 0;
473
0
    Stream_Read_UINT16(s, tmp);
474
0
    total += tmp;
475
0
  }
476
477
0
  if (total > 262144)
478
0
  {
479
0
    WLog_ERR(TAG,
480
0
             "TS_BITMAPCACHE_PERSISTENT_LIST_PDU::totalEntriesCacheX exceeds 262144 entries");
481
0
    return FALSE;
482
0
  }
483
484
0
  Stream_Read_UINT8(s, flags);
485
0
  if ((flags & ~(PERSIST_LAST_PDU | PERSIST_FIRST_PDU)) != 0)
486
0
  {
487
0
    WLog_ERR(TAG,
488
0
             "TS_BITMAPCACHE_PERSISTENT_LIST_PDU::bBitMask has an invalid value of 0x%02" PRIx8,
489
0
             flags);
490
0
    return FALSE;
491
0
  }
492
493
  /* Skip padding */
494
0
  if (!Stream_SafeSeek(s, 3))
495
0
  {
496
0
    WLog_ERR(TAG, "short TS_BITMAPCACHE_PERSISTENT_LIST_PDU, need 3 bytes, got %" PRIuz,
497
0
             Stream_GetRemainingLength(s));
498
0
    return FALSE;
499
0
  }
500
  /* Skip actual entries sent by client */
501
0
  if (!Stream_SafeSeek(s, count * sizeof(UINT64)))
502
0
  {
503
0
    WLog_ERR(TAG,
504
0
             "short TS_BITMAPCACHE_PERSISTENT_LIST_PDU, need %" PRIuz " bytes, got %" PRIuz,
505
0
             count * sizeof(UINT64), Stream_GetRemainingLength(s));
506
0
    return FALSE;
507
0
  }
508
0
  return TRUE;
509
0
}
510
511
static BOOL rdp_write_client_font_list_pdu(wStream* s, UINT16 flags)
512
0
{
513
0
  WINPR_ASSERT(s);
514
515
0
  if (!Stream_CheckAndLogRequiredCapacity(TAG, (s), 8))
516
0
    return FALSE;
517
0
  Stream_Write_UINT16(s, 0);     /* numberFonts (2 bytes) */
518
0
  Stream_Write_UINT16(s, 0);     /* totalNumFonts (2 bytes) */
519
0
  Stream_Write_UINT16(s, flags); /* listFlags (2 bytes) */
520
0
  Stream_Write_UINT16(s, 50);    /* entrySize (2 bytes) */
521
0
  return TRUE;
522
0
}
523
524
BOOL rdp_send_client_font_list_pdu(rdpRdp* rdp, UINT16 flags)
525
0
{
526
0
  UINT16 sec_flags = 0;
527
0
  wStream* s = rdp_data_pdu_init(rdp, &sec_flags);
528
0
  if (!s)
529
0
    return FALSE;
530
0
  if (!rdp_write_client_font_list_pdu(s, flags))
531
0
  {
532
0
    Stream_Free(s, TRUE);
533
0
    return FALSE;
534
0
  }
535
536
0
  WINPR_ASSERT(rdp->mcs);
537
0
  return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_FONT_LIST, rdp->mcs->userId, sec_flags);
538
0
}
539
540
BOOL rdp_recv_font_map_pdu(rdpRdp* rdp, wStream* s)
541
9.12k
{
542
9.12k
  UINT16 numberEntries = 0;
543
9.12k
  UINT16 totalNumEntries = 0;
544
9.12k
  UINT16 mapFlags = 0;
545
9.12k
  UINT16 entrySize = 0;
546
547
9.12k
  WINPR_ASSERT(rdp);
548
9.12k
  WINPR_ASSERT(rdp->settings);
549
9.12k
  WINPR_ASSERT(s);
550
9.12k
  WINPR_ASSERT(!freerdp_settings_get_bool(rdp->settings, FreeRDP_ServerMode));
551
552
  /* Do not fail here, see https://github.com/FreeRDP/FreeRDP/issues/925 */
553
9.12k
  if (Stream_CheckAndLogRequiredLength(TAG, s, 8))
554
729
  {
555
729
    Stream_Read_UINT16(s, numberEntries); /* numberEntries (2 bytes) */
556
729
    if (numberEntries != 0)
557
426
      WLog_WARN(
558
729
          TAG,
559
729
          "[MS-RDPBCGR] 2.2.1.22.1 Font Map PDU Data (TS_FONT_MAP_PDU)::numberEntries != 0 "
560
729
          "[%" PRIu16 "]",
561
729
          numberEntries);
562
729
    Stream_Read_UINT16(s, totalNumEntries); /* totalNumEntries (2 bytes) */
563
729
    if (totalNumEntries != 0)
564
453
      WLog_WARN(
565
729
          TAG,
566
729
          "[MS-RDPBCGR] 2.2.1.22.1 Font Map PDU Data (TS_FONT_MAP_PDU)::totalNumEntries != "
567
729
          "0 [%" PRIu16 "]",
568
729
          totalNumEntries);
569
729
    Stream_Read_UINT16(s, mapFlags); /* mapFlags (2 bytes) */
570
729
    if (mapFlags != (FONTLIST_FIRST | FONTLIST_LAST))
571
722
      WLog_WARN(
572
729
          TAG,
573
729
          "[MS-RDPBCGR] 2.2.1.22.1 Font Map PDU Data (TS_FONT_MAP_PDU)::mapFlags != 0x0003 "
574
729
          "(FONTLIST_FIRST | FONTLIST_LAST) "
575
729
          "[0x%04" PRIx16 "]",
576
729
          mapFlags);
577
729
    Stream_Read_UINT16(s, entrySize); /* entrySize (2 bytes) */
578
729
    if (entrySize != 4)
579
723
      WLog_WARN(TAG,
580
729
                "[MS-RDPBCGR] 2.2.1.22.1 Font Map PDU Data (TS_FONT_MAP_PDU)::entrySize != 4 "
581
729
                "[%" PRIu16 "]",
582
729
                entrySize);
583
729
  }
584
8.39k
  else
585
8.39k
    WLog_WARN(TAG,
586
9.12k
              "[MS-RDPBCGR] 2.2.1.22.1 Font Map PDU Data (TS_FONT_MAP_PDU) paylaod size is "
587
9.12k
              "0 instead of 8");
588
589
9.12k
  return rdp_finalize_set_flag(rdp, FINALIZE_SC_FONT_MAP_PDU);
590
9.12k
}
591
592
BOOL rdp_send_server_font_map_pdu(rdpRdp* rdp)
593
0
{
594
0
  UINT16 sec_flags = 0;
595
0
  wStream* s = rdp_data_pdu_init(rdp, &sec_flags);
596
0
  if (!s)
597
0
    return FALSE;
598
0
  if (!Stream_CheckAndLogRequiredCapacity(TAG, (s), 8))
599
0
  {
600
0
    Stream_Free(s, TRUE);
601
0
    return FALSE;
602
0
  }
603
0
  Stream_Write_UINT16(s, 0);                              /* numberEntries (2 bytes) */
604
0
  Stream_Write_UINT16(s, 0);                              /* totalNumEntries (2 bytes) */
605
0
  Stream_Write_UINT16(s, FONTLIST_FIRST | FONTLIST_LAST); /* mapFlags (2 bytes) */
606
0
  Stream_Write_UINT16(s, 4);                              /* entrySize (2 bytes) */
607
608
0
  WINPR_ASSERT(rdp->mcs);
609
0
  return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_FONT_MAP, rdp->mcs->userId, sec_flags);
610
0
}
611
612
BOOL rdp_recv_deactivate_all(rdpRdp* rdp, wStream* s)
613
17.2k
{
614
17.2k
  UINT16 lengthSourceDescriptor = 0;
615
616
17.2k
  WINPR_ASSERT(rdp);
617
17.2k
  WINPR_ASSERT(s);
618
619
17.2k
  if (rdp_get_state(rdp) == CONNECTION_STATE_ACTIVE)
620
0
  {
621
0
    if (!rdp_finalize_set_flag(rdp, FINALIZE_DEACTIVATE_REACTIVATE))
622
0
      return FALSE;
623
624
0
    rdp->was_deactivated = TRUE;
625
0
    rdp->deactivated_height = freerdp_settings_get_uint32(rdp->settings, FreeRDP_DesktopHeight);
626
0
    rdp->deactivated_width = freerdp_settings_get_uint32(rdp->settings, FreeRDP_DesktopWidth);
627
0
  }
628
629
  /*
630
   * Windows XP can send short DEACTIVATE_ALL PDU that doesn't contain
631
   * the following fields.
632
   */
633
634
17.2k
  WINPR_ASSERT(rdp->settings);
635
17.2k
  if (Stream_GetRemainingLength(s) > 0)
636
5.52k
  {
637
5.52k
    do
638
5.52k
    {
639
5.52k
      UINT32 ShareId = 0;
640
5.52k
      if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
641
2.29k
        break;
642
643
3.22k
      Stream_Read_UINT32(s, ShareId); /* shareId (4 bytes) */
644
3.22k
      if (!freerdp_settings_set_uint32(rdp->settings, FreeRDP_ShareId, ShareId))
645
0
        return FALSE;
646
647
3.22k
      if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
648
153
        break;
649
650
3.07k
      Stream_Read_UINT16(s, lengthSourceDescriptor); /* lengthSourceDescriptor (2 bytes) */
651
652
3.07k
      if (!Stream_CheckAndLogRequiredLength(TAG, s, lengthSourceDescriptor))
653
1.24k
        break;
654
655
1.83k
      Stream_Seek(s, lengthSourceDescriptor); /* sourceDescriptor (should be 0x00) */
656
1.83k
    } while (0);
657
5.52k
  }
658
659
17.2k
  return rdp_client_transition_to_state(rdp,
660
17.2k
                                        CONNECTION_STATE_CAPABILITIES_EXCHANGE_DEMAND_ACTIVE);
661
17.2k
}
662
663
BOOL rdp_send_deactivate_all(rdpRdp* rdp)
664
0
{
665
0
  WINPR_ASSERT(rdp);
666
0
  WINPR_ASSERT(rdp->mcs);
667
668
0
  if (rdp->mcs->userId == 0)
669
0
  {
670
0
    WLog_Print(rdp->log, WLOG_WARN,
671
0
               "rdpMcs::userId == 0, skip sending PDU_TYPE_DEACTIVATE_ALL");
672
0
    return TRUE;
673
0
  }
674
675
0
  UINT16 sec_flags = 0;
676
0
  wStream* s = rdp_send_stream_pdu_init(rdp, &sec_flags);
677
0
  BOOL status = FALSE;
678
679
0
  if (!s)
680
0
    return FALSE;
681
682
0
  if (!Stream_CheckAndLogRequiredCapacityWLog(rdp->log, (s), 7))
683
0
    goto fail;
684
685
0
  WINPR_ASSERT(rdp->settings);
686
0
  {
687
0
    const UINT32 ShareId = freerdp_settings_get_uint32(rdp->settings, FreeRDP_ShareId);
688
0
    Stream_Write_UINT32(s, ShareId); /* shareId (4 bytes) */
689
0
  }
690
0
  Stream_Write_UINT16(s, 1); /* lengthSourceDescriptor (2 bytes) */
691
0
  Stream_Write_UINT8(s, 0);  /* sourceDescriptor (should be 0x00) */
692
693
0
  WINPR_ASSERT(rdp->mcs);
694
0
  status = rdp_send_pdu(rdp, s, PDU_TYPE_DEACTIVATE_ALL, rdp->mcs->userId, sec_flags);
695
0
fail:
696
0
  Stream_Release(s);
697
0
  return status;
698
0
}
699
700
BOOL rdp_server_accept_client_control_pdu(rdpRdp* rdp, wStream* s)
701
0
{
702
0
  UINT16 action = 0;
703
0
  UINT16 GrantId = 0;
704
0
  UINT32 ControlId = 0;
705
0
  const CONNECTION_STATE state = rdp_get_state(rdp);
706
707
0
  WINPR_ASSERT(rdp);
708
0
  WINPR_ASSERT(s);
709
710
0
  if (!rdp_recv_control_pdu(s, &action, &GrantId, &ControlId))
711
0
    return FALSE;
712
713
0
  switch (action)
714
0
  {
715
716
0
    case CTRLACTION_REQUEST_CONTROL:
717
0
      if (!rdp_finalize_is_flag_set(rdp, FINALIZE_CS_CONTROL_COOPERATE_PDU))
718
0
      {
719
0
        char abuffer[128] = { 0 };
720
0
        char buffer[1024] = { 0 };
721
0
        WLog_WARN(TAG,
722
0
                  "Received action=%s with GrantId=0x%04" PRIx16 ", ControlId=0x%08" PRIx32
723
0
                  " in unexpected state %s [missing %s]",
724
0
                  rdp_ctrlaction_string(action, abuffer, sizeof(abuffer)), GrantId,
725
0
                  ControlId, rdp_state_string(state),
726
0
                  rdp_finalize_flags_to_str(FINALIZE_CS_CONTROL_COOPERATE_PDU, buffer,
727
0
                                            sizeof(buffer)));
728
0
        return FALSE;
729
0
      }
730
0
      if ((GrantId != 0) || (ControlId != 0))
731
0
      {
732
0
        WLog_WARN(TAG,
733
0
                  "Received CTRLACTION_COOPERATE with GrantId=0x%04" PRIx16
734
0
                  " != 0x00, ControlId=0x%08" PRIx32 " != 0x00",
735
0
                  GrantId, ControlId);
736
0
        return FALSE;
737
0
      }
738
0
      return rdp_finalize_set_flag(rdp, FINALIZE_CS_CONTROL_REQUEST_PDU);
739
0
    case CTRLACTION_COOPERATE:
740
0
      if (!rdp_finalize_is_flag_set(rdp, FINALIZE_CS_SYNCHRONIZE_PDU))
741
0
      {
742
0
        char abuffer[128] = { 0 };
743
0
        char buffer[1024] = { 0 };
744
0
        WLog_WARN(
745
0
            TAG,
746
0
            "Received action=%s with GrantId=0x%04" PRIx16 ", ControlId=0x%08" PRIx32
747
0
            " in unexpected state %s [missing %s]",
748
0
            rdp_ctrlaction_string(action, abuffer, sizeof(abuffer)), GrantId, ControlId,
749
0
            rdp_state_string(state),
750
0
            rdp_finalize_flags_to_str(FINALIZE_CS_SYNCHRONIZE_PDU, buffer, sizeof(buffer)));
751
0
        return FALSE;
752
0
      }
753
0
      if ((GrantId != 0) || (ControlId != 0))
754
0
      {
755
0
        WLog_WARN(TAG,
756
0
                  "Received CTRLACTION_COOPERATE with GrantId=0x%04" PRIx16
757
0
                  " != 0x00, ControlId=0x%08" PRIx32 " != 0x00",
758
0
                  GrantId, ControlId);
759
0
        return FALSE;
760
0
      }
761
0
      return rdp_finalize_set_flag(rdp, FINALIZE_CS_CONTROL_COOPERATE_PDU);
762
0
    default:
763
0
    {
764
0
      char abuffer[128] = { 0 };
765
0
      WLog_WARN(TAG,
766
0
                "Received unexpected action=%s with GrantId=0x%04" PRIx16
767
0
                ", ControlId=0x%08" PRIx32,
768
0
                rdp_ctrlaction_string(action, abuffer, sizeof(abuffer)), GrantId, ControlId);
769
0
      return FALSE;
770
0
    }
771
0
  }
772
773
0
  return TRUE;
774
0
}
775
776
BOOL rdp_server_accept_client_font_list_pdu(rdpRdp* rdp, wStream* s)
777
0
{
778
0
  WINPR_ASSERT(rdp);
779
0
  WINPR_ASSERT(s);
780
781
0
  if (!rdp_recv_client_font_list_pdu(s))
782
0
    return FALSE;
783
0
  rdp_finalize_set_flag(rdp, FINALIZE_CS_FONT_LIST_PDU);
784
785
0
  if (!rdp_server_transition_to_state(rdp, CONNECTION_STATE_FINALIZATION_CLIENT_FONT_MAP))
786
0
    return FALSE;
787
788
0
  if (!rdp_send_server_font_map_pdu(rdp))
789
0
    return FALSE;
790
791
0
  if (!rdp_server_transition_to_state(rdp, CONNECTION_STATE_ACTIVE))
792
0
    return FALSE;
793
794
0
  return TRUE;
795
0
}
796
797
BOOL rdp_server_accept_client_persistent_key_list_pdu(rdpRdp* rdp, wStream* s)
798
0
{
799
0
  WINPR_ASSERT(rdp);
800
0
  WINPR_ASSERT(s);
801
802
0
  if (!rdp_recv_client_persistent_key_list_pdu(s))
803
0
    return FALSE;
804
805
0
  rdp_finalize_set_flag(rdp, FINALIZE_CS_PERSISTENT_KEY_LIST_PDU);
806
  // TODO: Actually do something with this
807
0
  return TRUE;
808
0
}
809
810
const char* rdp_ctrlaction_string(UINT16 action, char* buffer, size_t size)
811
45
{
812
45
  const char* actstr = NULL;
813
45
  switch (action)
814
45
  {
815
0
    case CTRLACTION_COOPERATE:
816
0
      actstr = "CTRLACTION_COOPERATE";
817
0
      break;
818
0
    case CTRLACTION_DETACH:
819
0
      actstr = "CTRLACTION_DETACH";
820
0
      break;
821
0
    case CTRLACTION_GRANTED_CONTROL:
822
0
      actstr = "CTRLACTION_GRANTED_CONTROL";
823
0
      break;
824
0
    case CTRLACTION_REQUEST_CONTROL:
825
0
      actstr = "CTRLACTION_REQUEST_CONTROL";
826
0
      break;
827
45
    default:
828
45
      actstr = "CTRLACTION_UNKNOWN";
829
45
      break;
830
45
  }
831
832
45
  (void)_snprintf(buffer, size, "%s [0x%04" PRIx16 "]", actstr, action);
833
45
  return buffer;
834
45
}