Coverage Report

Created: 2026-10-15 06:31

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
30.2k
#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
30.2k
{
50
30.2k
  UINT16 msgType = 0;
51
30.2k
  UINT16 targetUser = 0;
52
53
30.2k
  WINPR_UNUSED(rdp);
54
30.2k
  if (!Stream_CheckAndLogRequiredLengthEx(TAG, WLOG_WARN, s, 4, 1, "%s(%s:%" PRIuz ") %s",
55
30.2k
                                          __func__, __FILE__, (size_t)__LINE__, what))
56
24.3k
    return FALSE;
57
5.82k
  Stream_Read_UINT16(s, msgType);
58
5.82k
  if (msgType != SYNCMSGTYPE_SYNC)
59
5.70k
  {
60
5.70k
    WLog_WARN(TAG, "%s: Invalid messageType=0x%04" PRIx16 ", expected 0x%04" PRIx16, what,
61
5.70k
              msgType, WINPR_CXX_COMPAT_CAST(uint16_t, SYNCMSGTYPE_SYNC));
62
5.70k
    return FALSE;
63
5.70k
  }
64
125
  Stream_Read_UINT16(s, targetUser);
65
125
  WLog_VRB(TAG, "%s: targetUser=0x%04" PRIx16, what, targetUser);
66
125
  return TRUE;
67
5.82k
}
68
69
BOOL rdp_recv_server_synchronize_pdu(rdpRdp* rdp, wStream* s)
70
15.1k
{
71
15.1k
  if (!rdp_recv_sync_pdu(rdp, s, "[MS-RDPBCGR] 2.2.1.19 Server Synchronize PDU"))
72
15.0k
    return FALSE;
73
66
  return rdp_finalize_set_flag(rdp, FINALIZE_SC_SYNCHRONIZE_PDU);
74
15.1k
}
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
15.0k
{
96
15.0k
  if (!rdp_recv_sync_pdu(rdp, s, "[MS-RDPBCGR] 2.2.1.14 Client Synchronize PDU"))
97
15.0k
    return FALSE;
98
59
  return rdp_finalize_set_flag(rdp, FINALIZE_CS_SYNCHRONIZE_PDU);
99
15.0k
}
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
25
{
121
25
  WINPR_ASSERT(s);
122
25
  WINPR_ASSERT(action);
123
25
  WINPR_ASSERT(grantId);
124
25
  WINPR_ASSERT(controlId);
125
126
25
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
127
1
    return FALSE;
128
129
24
  Stream_Read_UINT16(s, *action);    /* action (2 bytes) */
130
24
  Stream_Read_UINT16(s, *grantId);   /* grantId (2 bytes) */
131
24
  Stream_Read_UINT32(s, *controlId); /* controlId (4 bytes) */
132
24
  return TRUE;
133
25
}
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
25
{
149
25
  UINT16 action = 0;
150
25
  UINT16 grantId = 0;
151
25
  UINT32 controlId = 0;
152
153
25
  WINPR_ASSERT(rdp);
154
25
  WINPR_ASSERT(s);
155
156
25
  if (!rdp_recv_control_pdu(s, &action, &grantId, &controlId))
157
1
    return FALSE;
158
159
24
  switch (action)
160
24
  {
161
3
    case CTRLACTION_COOPERATE:
162
3
      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
20
    default:
168
20
    {
169
20
      char buffer[128] = WINPR_C_ARRAY_INIT;
170
20
      WLog_WARN(TAG, "Unexpected control PDU %s",
171
20
                rdp_ctrlaction_string(action, buffer, sizeof(buffer)));
172
173
20
      return FALSE;
174
0
    }
175
24
  }
176
24
}
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 = nullptr;
287
0
  rdpPersistentCache* persistent = nullptr;
288
0
  rdpSettings* settings = rdp->settings;
289
290
0
  *pKeyList = nullptr;
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 = WINPR_C_ARRAY_INIT;
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 = nullptr;
345
0
  RDP_BITMAP_PERSISTENT_INFO info = WINPR_C_ARRAY_INIT;
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
0
  const UINT32 cellInfoCount =
354
0
      freerdp_settings_get_uint32(settings, FreeRDP_BitmapCacheV2NumCells);
355
0
  if (cellInfoCount != 5)
356
0
  {
357
0
    WLog_ERR(TAG, "BitmapCacheV2NumCells %" PRIu32 ", but must be 5. Aborting.", cellInfoCount);
358
0
    free(keyList);
359
0
    return FALSE;
360
0
  }
361
  // MS-RDPBCGR recommends sending no more than 169 entries at once.
362
  // In practice, sending more than 2042 entries at once triggers an error.
363
  // It should be possible to advertise the entire client bitmap cache
364
  // by sending multiple persistent key list PDUs, but the current code
365
  // only bothers sending a single, smaller list of entries instead.
366
367
0
  if (keyCount > keyMaxFrag)
368
0
    keyCount = keyMaxFrag;
369
370
0
  WINPR_ASSERT(settings->BitmapCacheV2CellInfo[0].numEntries <= UINT16_MAX);
371
0
  info.totalEntriesCache0 = (UINT16)settings->BitmapCacheV2CellInfo[0].numEntries;
372
373
0
  WINPR_ASSERT(settings->BitmapCacheV2CellInfo[1].numEntries <= UINT16_MAX);
374
0
  info.totalEntriesCache1 = (UINT16)settings->BitmapCacheV2CellInfo[1].numEntries;
375
376
0
  WINPR_ASSERT(settings->BitmapCacheV2CellInfo[2].numEntries <= UINT16_MAX);
377
0
  info.totalEntriesCache2 = (UINT16)settings->BitmapCacheV2CellInfo[2].numEntries;
378
379
0
  WINPR_ASSERT(settings->BitmapCacheV2CellInfo[3].numEntries <= UINT16_MAX);
380
0
  info.totalEntriesCache3 = (UINT16)settings->BitmapCacheV2CellInfo[3].numEntries;
381
382
0
  WINPR_ASSERT(settings->BitmapCacheV2CellInfo[4].numEntries <= UINT16_MAX);
383
0
  info.totalEntriesCache4 = (UINT16)settings->BitmapCacheV2CellInfo[4].numEntries;
384
385
0
  info.numEntriesCache0 = MIN(keyCount, info.totalEntriesCache0);
386
0
  keyCount -= info.numEntriesCache0;
387
0
  info.numEntriesCache1 = MIN(keyCount, info.totalEntriesCache1);
388
0
  keyCount -= info.numEntriesCache1;
389
0
  info.numEntriesCache2 = MIN(keyCount, info.totalEntriesCache2);
390
0
  keyCount -= info.numEntriesCache2;
391
0
  info.numEntriesCache3 = MIN(keyCount, info.totalEntriesCache3);
392
0
  keyCount -= info.numEntriesCache3;
393
0
  info.numEntriesCache4 = MIN(keyCount, info.totalEntriesCache4);
394
395
0
  info.totalEntriesCache0 = info.numEntriesCache0;
396
0
  info.totalEntriesCache1 = info.numEntriesCache1;
397
0
  info.totalEntriesCache2 = info.numEntriesCache2;
398
0
  info.totalEntriesCache3 = info.numEntriesCache3;
399
0
  info.totalEntriesCache4 = info.numEntriesCache4;
400
401
0
  keyCount = info.totalEntriesCache0 + info.totalEntriesCache1 + info.totalEntriesCache2 +
402
0
             info.totalEntriesCache3 + info.totalEntriesCache4;
403
404
0
  info.keyCount = keyCount;
405
0
  info.keyList = keyList;
406
407
0
  WLog_DBG(TAG, "persistentKeyList count: %" PRIu32, info.keyCount);
408
409
0
  WLog_DBG(TAG,
410
0
           "numEntriesCache: [0]: %" PRIu16 " [1]: %" PRIu16 " [2]: %" PRIu16 " [3]: %" PRIu16
411
0
           " [4]: %" PRIu16,
412
0
           info.numEntriesCache0, info.numEntriesCache1, info.numEntriesCache2,
413
0
           info.numEntriesCache3, info.numEntriesCache4);
414
415
0
  WLog_DBG(TAG,
416
0
           "totalEntriesCache: [0]: %" PRIu16 " [1]: %" PRIu16 " [2]: %" PRIu16 " [3]: %" PRIu16
417
0
           " [4]: %" PRIu16,
418
0
           info.totalEntriesCache0, info.totalEntriesCache1, info.totalEntriesCache2,
419
0
           info.totalEntriesCache3, info.totalEntriesCache4);
420
421
0
  UINT16 sec_flags = 0;
422
0
  wStream* s = rdp_data_pdu_init(rdp, &sec_flags);
423
424
0
  if (!s)
425
0
  {
426
0
    free(keyList);
427
0
    return FALSE;
428
0
  }
429
430
0
  if (!rdp_write_client_persistent_key_list_pdu(s, &info))
431
0
  {
432
0
    Stream_Free(s, TRUE);
433
0
    free(keyList);
434
0
    return FALSE;
435
0
  }
436
437
0
  WINPR_ASSERT(rdp->mcs);
438
0
  free(keyList);
439
440
0
  return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_BITMAP_CACHE_PERSISTENT_LIST, rdp->mcs->userId,
441
0
                           sec_flags);
442
0
}
443
444
BOOL rdp_recv_client_font_list_pdu(wStream* s)
445
0
{
446
0
  WINPR_ASSERT(s);
447
  /* 2.2.1.18 Client Font List PDU */
448
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
449
0
    return FALSE;
450
451
0
  return Stream_SafeSeek(s, 8);
452
0
}
453
454
BOOL rdp_recv_client_persistent_key_list_pdu(wStream* s)
455
0
{
456
0
  BYTE flags = 0;
457
0
  size_t count = 0;
458
0
  size_t total = 0;
459
0
  UINT16 cache = 0;
460
461
0
  WINPR_ASSERT(s);
462
463
  /* 2.2.1.17.1 Persistent Key List PDU Data (TS_BITMAPCACHE_PERSISTENT_LIST_PDU) */
464
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 21))
465
0
  {
466
0
    WLog_ERR(TAG, "short TS_BITMAPCACHE_PERSISTENT_LIST_PDU, need 21 bytes, got %" PRIuz,
467
0
             Stream_GetRemainingLength(s));
468
0
    return FALSE;
469
0
  }
470
  /* Read numEntriesCacheX for variable length data in PDU */
471
0
  for (size_t x = 0; x < 5; x++)
472
0
  {
473
0
    Stream_Read_UINT16(s, cache);
474
0
    count += cache;
475
0
  }
476
477
  /* Skip totalEntriesCacheX */
478
0
  for (size_t x = 0; x < 5; x++)
479
0
  {
480
0
    UINT16 tmp = 0;
481
0
    Stream_Read_UINT16(s, tmp);
482
0
    total += tmp;
483
0
  }
484
485
0
  if (total > 262144)
486
0
  {
487
0
    WLog_ERR(TAG,
488
0
             "TS_BITMAPCACHE_PERSISTENT_LIST_PDU::totalEntriesCacheX exceeds 262144 entries");
489
0
    return FALSE;
490
0
  }
491
492
0
  Stream_Read_UINT8(s, flags);
493
0
  if ((flags & ~(PERSIST_LAST_PDU | PERSIST_FIRST_PDU)) != 0)
494
0
  {
495
0
    WLog_ERR(TAG,
496
0
             "TS_BITMAPCACHE_PERSISTENT_LIST_PDU::bBitMask has an invalid value of 0x%02" PRIx8,
497
0
             flags);
498
0
    return FALSE;
499
0
  }
500
501
  /* Skip padding */
502
0
  if (!Stream_SafeSeek(s, 3))
503
0
  {
504
0
    WLog_ERR(TAG, "short TS_BITMAPCACHE_PERSISTENT_LIST_PDU, need 3 bytes, got %" PRIuz,
505
0
             Stream_GetRemainingLength(s));
506
0
    return FALSE;
507
0
  }
508
  /* Skip actual entries sent by client */
509
0
  if (!Stream_SafeSeek(s, count * sizeof(UINT64)))
510
0
  {
511
0
    WLog_ERR(TAG,
512
0
             "short TS_BITMAPCACHE_PERSISTENT_LIST_PDU, need %" PRIuz " bytes, got %" PRIuz,
513
0
             count * sizeof(UINT64), Stream_GetRemainingLength(s));
514
0
    return FALSE;
515
0
  }
516
0
  return TRUE;
517
0
}
518
519
static BOOL rdp_write_client_font_list_pdu(wStream* s, UINT16 flags)
520
0
{
521
0
  WINPR_ASSERT(s);
522
523
0
  if (!Stream_CheckAndLogRequiredCapacity(TAG, (s), 8))
524
0
    return FALSE;
525
0
  Stream_Write_UINT16(s, 0);     /* numberFonts (2 bytes) */
526
0
  Stream_Write_UINT16(s, 0);     /* totalNumFonts (2 bytes) */
527
0
  Stream_Write_UINT16(s, flags); /* listFlags (2 bytes) */
528
0
  Stream_Write_UINT16(s, 50);    /* entrySize (2 bytes) */
529
0
  return TRUE;
530
0
}
531
532
BOOL rdp_send_client_font_list_pdu(rdpRdp* rdp, UINT16 flags)
533
0
{
534
0
  UINT16 sec_flags = 0;
535
0
  wStream* s = rdp_data_pdu_init(rdp, &sec_flags);
536
0
  if (!s)
537
0
    return FALSE;
538
0
  if (!rdp_write_client_font_list_pdu(s, flags))
539
0
  {
540
0
    Stream_Free(s, TRUE);
541
0
    return FALSE;
542
0
  }
543
544
0
  WINPR_ASSERT(rdp->mcs);
545
0
  return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_FONT_LIST, rdp->mcs->userId, sec_flags);
546
0
}
547
548
BOOL rdp_recv_font_map_pdu(rdpRdp* rdp, wStream* s)
549
8.03k
{
550
8.03k
  UINT16 numberEntries = 0;
551
8.03k
  UINT16 totalNumEntries = 0;
552
8.03k
  UINT16 mapFlags = 0;
553
8.03k
  UINT16 entrySize = 0;
554
555
8.03k
  WINPR_ASSERT(rdp);
556
8.03k
  WINPR_ASSERT(rdp->settings);
557
8.03k
  WINPR_ASSERT(s);
558
8.03k
  WINPR_ASSERT(!freerdp_settings_get_bool(rdp->settings, FreeRDP_ServerMode));
559
560
8.03k
  if (!rdp_has_reached_state(rdp, CONNECTION_STATE_FINALIZATION_CLIENT_FONT_MAP))
561
3
    return FALSE;
562
563
  /* Do not fail here, see https://github.com/FreeRDP/FreeRDP/issues/925 */
564
8.03k
  if (Stream_CheckAndLogRequiredLength(TAG, s, 8))
565
693
  {
566
693
    Stream_Read_UINT16(s, numberEntries); /* numberEntries (2 bytes) */
567
693
    if (numberEntries != 0)
568
394
      WLog_WARN(
569
693
          TAG,
570
693
          "[MS-RDPBCGR] 2.2.1.22.1 Font Map PDU Data (TS_FONT_MAP_PDU)::numberEntries != 0 "
571
693
          "[%" PRIu16 "]",
572
693
          numberEntries);
573
693
    Stream_Read_UINT16(s, totalNumEntries); /* totalNumEntries (2 bytes) */
574
693
    if (totalNumEntries != 0)
575
415
      WLog_WARN(
576
693
          TAG,
577
693
          "[MS-RDPBCGR] 2.2.1.22.1 Font Map PDU Data (TS_FONT_MAP_PDU)::totalNumEntries != "
578
693
          "0 [%" PRIu16 "]",
579
693
          totalNumEntries);
580
693
    Stream_Read_UINT16(s, mapFlags); /* mapFlags (2 bytes) */
581
693
    if (mapFlags != (FONTLIST_FIRST | FONTLIST_LAST))
582
691
      WLog_WARN(
583
693
          TAG,
584
693
          "[MS-RDPBCGR] 2.2.1.22.1 Font Map PDU Data (TS_FONT_MAP_PDU)::mapFlags != 0x0003 "
585
693
          "(FONTLIST_FIRST | FONTLIST_LAST) "
586
693
          "[0x%04" PRIx16 "]",
587
693
          mapFlags);
588
693
    Stream_Read_UINT16(s, entrySize); /* entrySize (2 bytes) */
589
693
    if (entrySize != 4)
590
691
      WLog_WARN(TAG,
591
693
                "[MS-RDPBCGR] 2.2.1.22.1 Font Map PDU Data (TS_FONT_MAP_PDU)::entrySize != 4 "
592
693
                "[%" PRIu16 "]",
593
693
                entrySize);
594
693
  }
595
7.34k
  else
596
7.34k
    WLog_WARN(TAG,
597
8.03k
              "[MS-RDPBCGR] 2.2.1.22.1 Font Map PDU Data (TS_FONT_MAP_PDU) paylaod size is "
598
8.03k
              "0 instead of 8");
599
600
8.03k
  return rdp_finalize_set_flag(rdp, FINALIZE_SC_FONT_MAP_PDU);
601
8.03k
}
602
603
BOOL rdp_send_server_font_map_pdu(rdpRdp* rdp)
604
0
{
605
0
  UINT16 sec_flags = 0;
606
0
  wStream* s = rdp_data_pdu_init(rdp, &sec_flags);
607
0
  if (!s)
608
0
    return FALSE;
609
0
  if (!Stream_CheckAndLogRequiredCapacity(TAG, (s), 8))
610
0
  {
611
0
    Stream_Free(s, TRUE);
612
0
    return FALSE;
613
0
  }
614
0
  Stream_Write_UINT16(s, 0);                              /* numberEntries (2 bytes) */
615
0
  Stream_Write_UINT16(s, 0);                              /* totalNumEntries (2 bytes) */
616
0
  Stream_Write_UINT16(s, FONTLIST_FIRST | FONTLIST_LAST); /* mapFlags (2 bytes) */
617
0
  Stream_Write_UINT16(s, 4);                              /* entrySize (2 bytes) */
618
619
0
  WINPR_ASSERT(rdp->mcs);
620
0
  return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_FONT_MAP, rdp->mcs->userId, sec_flags);
621
0
}
622
623
BOOL rdp_recv_deactivate_all(rdpRdp* rdp, wStream* s)
624
15.0k
{
625
15.0k
  UINT16 lengthSourceDescriptor = 0;
626
627
15.0k
  WINPR_ASSERT(rdp);
628
15.0k
  WINPR_ASSERT(s);
629
630
15.0k
  if (rdp_get_state(rdp) == CONNECTION_STATE_ACTIVE)
631
8.03k
  {
632
8.03k
    if (!rdp_finalize_set_flag(rdp, FINALIZE_DEACTIVATE_REACTIVATE))
633
0
      return FALSE;
634
635
8.03k
    rdp->was_deactivated = TRUE;
636
8.03k
    rdp->deactivated_height = freerdp_settings_get_uint32(rdp->settings, FreeRDP_DesktopHeight);
637
8.03k
    rdp->deactivated_width = freerdp_settings_get_uint32(rdp->settings, FreeRDP_DesktopWidth);
638
8.03k
  }
639
640
  /*
641
   * Windows XP can send short DEACTIVATE_ALL PDU that doesn't contain
642
   * the following fields.
643
   */
644
645
15.0k
  WINPR_ASSERT(rdp->settings);
646
15.0k
  if (Stream_GetRemainingLength(s) > 0)
647
7.23k
  {
648
7.23k
    do
649
7.23k
    {
650
7.23k
      UINT32 ShareId = 0;
651
7.23k
      if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
652
3.75k
        break;
653
654
3.48k
      Stream_Read_UINT32(s, ShareId); /* shareId (4 bytes) */
655
3.48k
      if (!freerdp_settings_set_uint32(rdp->settings, FreeRDP_ShareId, ShareId))
656
0
        return FALSE;
657
658
3.48k
      if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
659
378
        break;
660
661
3.10k
      Stream_Read_UINT16(s, lengthSourceDescriptor); /* lengthSourceDescriptor (2 bytes) */
662
663
3.10k
      if (!Stream_CheckAndLogRequiredLength(TAG, s, lengthSourceDescriptor))
664
1.43k
        break;
665
666
1.66k
      Stream_Seek(s, lengthSourceDescriptor); /* sourceDescriptor (should be 0x00) */
667
1.66k
    } while (0);
668
7.23k
  }
669
670
15.0k
  return rdp_client_transition_to_state(rdp,
671
15.0k
                                        CONNECTION_STATE_CAPABILITIES_EXCHANGE_DEMAND_ACTIVE);
672
15.0k
}
673
674
BOOL rdp_send_deactivate_all(rdpRdp* rdp)
675
0
{
676
0
  WINPR_ASSERT(rdp);
677
0
  WINPR_ASSERT(rdp->mcs);
678
679
0
  if (rdp->mcs->userId == 0)
680
0
  {
681
0
    WLog_Print(rdp->log, WLOG_WARN,
682
0
               "rdpMcs::userId == 0, skip sending PDU_TYPE_DEACTIVATE_ALL");
683
0
    return TRUE;
684
0
  }
685
686
0
  UINT16 sec_flags = 0;
687
0
  wStream* s = rdp_send_stream_pdu_init(rdp, &sec_flags);
688
0
  BOOL status = FALSE;
689
690
0
  if (!s)
691
0
    return FALSE;
692
693
0
  if (!Stream_CheckAndLogRequiredCapacityWLog(rdp->log, (s), 7))
694
0
    goto fail;
695
696
0
  WINPR_ASSERT(rdp->settings);
697
0
  {
698
0
    const UINT32 ShareId = freerdp_settings_get_uint32(rdp->settings, FreeRDP_ShareId);
699
0
    Stream_Write_UINT32(s, ShareId); /* shareId (4 bytes) */
700
0
  }
701
0
  Stream_Write_UINT16(s, 1); /* lengthSourceDescriptor (2 bytes) */
702
0
  Stream_Write_UINT8(s, 0);  /* sourceDescriptor (should be 0x00) */
703
704
0
  WINPR_ASSERT(rdp->mcs);
705
0
  status = rdp_send_pdu(rdp, s, PDU_TYPE_DEACTIVATE_ALL, rdp->mcs->userId, sec_flags);
706
0
fail:
707
0
  Stream_Release(s);
708
0
  return status;
709
0
}
710
711
BOOL rdp_server_accept_client_control_pdu(rdpRdp* rdp, wStream* s)
712
0
{
713
0
  UINT16 action = 0;
714
0
  UINT16 GrantId = 0;
715
0
  UINT32 ControlId = 0;
716
0
  const CONNECTION_STATE state = rdp_get_state(rdp);
717
718
0
  WINPR_ASSERT(rdp);
719
0
  WINPR_ASSERT(s);
720
721
0
  if (!rdp_recv_control_pdu(s, &action, &GrantId, &ControlId))
722
0
    return FALSE;
723
724
0
  switch (action)
725
0
  {
726
727
0
    case CTRLACTION_REQUEST_CONTROL:
728
0
      if (!rdp_finalize_is_flag_set(rdp, FINALIZE_CS_CONTROL_COOPERATE_PDU))
729
0
      {
730
0
        char abuffer[128] = WINPR_C_ARRAY_INIT;
731
0
        char buffer[1024] = WINPR_C_ARRAY_INIT;
732
0
        WLog_WARN(TAG,
733
0
                  "Received action=%s with GrantId=0x%04" PRIx16 ", ControlId=0x%08" PRIx32
734
0
                  " in unexpected state %s [missing %s]",
735
0
                  rdp_ctrlaction_string(action, abuffer, sizeof(abuffer)), GrantId,
736
0
                  ControlId, rdp_state_string(state),
737
0
                  rdp_finalize_flags_to_str(FINALIZE_CS_CONTROL_COOPERATE_PDU, buffer,
738
0
                                            sizeof(buffer)));
739
0
        return FALSE;
740
0
      }
741
0
      if ((GrantId != 0) || (ControlId != 0))
742
0
      {
743
0
        WLog_WARN(TAG,
744
0
                  "Received CTRLACTION_COOPERATE with GrantId=0x%04" PRIx16
745
0
                  " != 0x00, ControlId=0x%08" PRIx32 " != 0x00",
746
0
                  GrantId, ControlId);
747
0
        return FALSE;
748
0
      }
749
0
      return rdp_finalize_set_flag(rdp, FINALIZE_CS_CONTROL_REQUEST_PDU);
750
0
    case CTRLACTION_COOPERATE:
751
0
      if (!rdp_finalize_is_flag_set(rdp, FINALIZE_CS_SYNCHRONIZE_PDU))
752
0
      {
753
0
        char abuffer[128] = WINPR_C_ARRAY_INIT;
754
0
        char buffer[1024] = WINPR_C_ARRAY_INIT;
755
0
        WLog_WARN(
756
0
            TAG,
757
0
            "Received action=%s with GrantId=0x%04" PRIx16 ", ControlId=0x%08" PRIx32
758
0
            " in unexpected state %s [missing %s]",
759
0
            rdp_ctrlaction_string(action, abuffer, sizeof(abuffer)), GrantId, ControlId,
760
0
            rdp_state_string(state),
761
0
            rdp_finalize_flags_to_str(FINALIZE_CS_SYNCHRONIZE_PDU, buffer, sizeof(buffer)));
762
0
        return FALSE;
763
0
      }
764
0
      if ((GrantId != 0) || (ControlId != 0))
765
0
      {
766
0
        WLog_WARN(TAG,
767
0
                  "Received CTRLACTION_COOPERATE with GrantId=0x%04" PRIx16
768
0
                  " != 0x00, ControlId=0x%08" PRIx32 " != 0x00",
769
0
                  GrantId, ControlId);
770
0
        return FALSE;
771
0
      }
772
0
      return rdp_finalize_set_flag(rdp, FINALIZE_CS_CONTROL_COOPERATE_PDU);
773
0
    default:
774
0
    {
775
0
      char abuffer[128] = WINPR_C_ARRAY_INIT;
776
0
      WLog_WARN(TAG,
777
0
                "Received unexpected action=%s with GrantId=0x%04" PRIx16
778
0
                ", ControlId=0x%08" PRIx32,
779
0
                rdp_ctrlaction_string(action, abuffer, sizeof(abuffer)), GrantId, ControlId);
780
0
      return FALSE;
781
0
    }
782
0
  }
783
784
0
  return TRUE;
785
0
}
786
787
BOOL rdp_server_accept_client_font_list_pdu(rdpRdp* rdp, wStream* s)
788
0
{
789
0
  WINPR_ASSERT(rdp);
790
0
  WINPR_ASSERT(s);
791
792
0
  if (!rdp_recv_client_font_list_pdu(s))
793
0
    return FALSE;
794
0
  rdp_finalize_set_flag(rdp, FINALIZE_CS_FONT_LIST_PDU);
795
796
0
  if (!rdp_server_transition_to_state(rdp, CONNECTION_STATE_FINALIZATION_CLIENT_FONT_MAP))
797
0
    return FALSE;
798
799
0
  if (!rdp_send_server_font_map_pdu(rdp))
800
0
    return FALSE;
801
802
0
  if (!rdp_server_transition_to_state(rdp, CONNECTION_STATE_ACTIVE))
803
0
    return FALSE;
804
805
0
  return TRUE;
806
0
}
807
808
BOOL rdp_server_accept_client_persistent_key_list_pdu(rdpRdp* rdp, wStream* s)
809
0
{
810
0
  WINPR_ASSERT(rdp);
811
0
  WINPR_ASSERT(s);
812
813
0
  if (!rdp_recv_client_persistent_key_list_pdu(s))
814
0
    return FALSE;
815
816
0
  rdp_finalize_set_flag(rdp, FINALIZE_CS_PERSISTENT_KEY_LIST_PDU);
817
  // TODO: Actually do something with this
818
0
  return TRUE;
819
0
}
820
821
const char* rdp_ctrlaction_string(UINT16 action, char* buffer, size_t size)
822
20
{
823
20
  const char* actstr = nullptr;
824
20
  switch (action)
825
20
  {
826
0
    case CTRLACTION_COOPERATE:
827
0
      actstr = "CTRLACTION_COOPERATE";
828
0
      break;
829
0
    case CTRLACTION_DETACH:
830
0
      actstr = "CTRLACTION_DETACH";
831
0
      break;
832
0
    case CTRLACTION_GRANTED_CONTROL:
833
0
      actstr = "CTRLACTION_GRANTED_CONTROL";
834
0
      break;
835
0
    case CTRLACTION_REQUEST_CONTROL:
836
0
      actstr = "CTRLACTION_REQUEST_CONTROL";
837
0
      break;
838
20
    default:
839
20
      actstr = "CTRLACTION_UNKNOWN";
840
20
      break;
841
20
  }
842
843
20
  (void)_snprintf(buffer, size, "%s [0x%04" PRIx16 "]", actstr, action);
844
20
  return buffer;
845
20
}