Coverage Report

Created: 2024-09-08 06:20

/src/FreeRDP/channels/cliprdr/cliprdr_common.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Cliprdr common
4
 *
5
 * Copyright 2013 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
 * Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
9
 *
10
 * Licensed under the Apache License, Version 2.0 (the "License");
11
 * you may not use this file except in compliance with the License.
12
 * You may obtain a copy of the License at
13
 *
14
 *     http://www.apache.org/licenses/LICENSE-2.0
15
 *
16
 * Unless required by applicable law or agreed to in writing, software
17
 * distributed under the License is distributed on an "AS IS" BASIS,
18
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
 * See the License for the specific language governing permissions and
20
 * limitations under the License.
21
 */
22
23
#include <winpr/crt.h>
24
#include <winpr/stream.h>
25
#include <freerdp/channels/log.h>
26
27
#define TAG CHANNELS_TAG("cliprdr.common")
28
29
#include "cliprdr_common.h"
30
31
static const char* CB_MSG_TYPE_STR(UINT32 type)
32
0
{
33
0
  switch (type)
34
0
  {
35
0
    case CB_MONITOR_READY:
36
0
      return "CB_MONITOR_READY";
37
0
    case CB_FORMAT_LIST:
38
0
      return "CB_FORMAT_LIST";
39
0
    case CB_FORMAT_LIST_RESPONSE:
40
0
      return "CB_FORMAT_LIST_RESPONSE";
41
0
    case CB_FORMAT_DATA_REQUEST:
42
0
      return "CB_FORMAT_DATA_REQUEST";
43
0
    case CB_FORMAT_DATA_RESPONSE:
44
0
      return "CB_FORMAT_DATA_RESPONSE";
45
0
    case CB_TEMP_DIRECTORY:
46
0
      return "CB_TEMP_DIRECTORY";
47
0
    case CB_CLIP_CAPS:
48
0
      return "CB_CLIP_CAPS";
49
0
    case CB_FILECONTENTS_REQUEST:
50
0
      return "CB_FILECONTENTS_REQUEST";
51
0
    case CB_FILECONTENTS_RESPONSE:
52
0
      return "CB_FILECONTENTS_RESPONSE";
53
0
    case CB_LOCK_CLIPDATA:
54
0
      return "CB_LOCK_CLIPDATA";
55
0
    case CB_UNLOCK_CLIPDATA:
56
0
      return "CB_UNLOCK_CLIPDATA";
57
0
    default:
58
0
      return "UNKNOWN";
59
0
  }
60
0
}
61
62
const char* CB_MSG_TYPE_STRING(UINT16 type, char* buffer, size_t size)
63
0
{
64
0
  (void)_snprintf(buffer, size, "%s [0x%04" PRIx16 "]", CB_MSG_TYPE_STR(type), type);
65
0
  return buffer;
66
0
}
67
68
const char* CB_MSG_FLAGS_STRING(UINT16 msgFlags, char* buffer, size_t size)
69
0
{
70
0
  if ((msgFlags & CB_RESPONSE_OK) != 0)
71
0
    winpr_str_append("CB_RESPONSE_OK", buffer, size, "|");
72
0
  if ((msgFlags & CB_RESPONSE_FAIL) != 0)
73
0
    winpr_str_append("CB_RESPONSE_FAIL", buffer, size, "|");
74
0
  if ((msgFlags & CB_ASCII_NAMES) != 0)
75
0
    winpr_str_append("CB_ASCII_NAMES", buffer, size, "|");
76
77
0
  const size_t len = strnlen(buffer, size);
78
0
  if (!len)
79
0
    winpr_str_append("NONE", buffer, size, "");
80
81
0
  char val[32] = { 0 };
82
0
  (void)_snprintf(val, sizeof(val), "[0x%04" PRIx16 "]", msgFlags);
83
0
  winpr_str_append(val, buffer, size, "|");
84
0
  return buffer;
85
0
}
86
87
static BOOL cliprdr_validate_file_contents_request(const CLIPRDR_FILE_CONTENTS_REQUEST* request)
88
0
{
89
  /*
90
   * [MS-RDPECLIP] 2.2.5.3 File Contents Request PDU (CLIPRDR_FILECONTENTS_REQUEST).
91
   *
92
   * A request for the size of the file identified by the lindex field. The size MUST be
93
   * returned as a 64-bit, unsigned integer. The cbRequested field MUST be set to
94
   * 0x00000008 and both the nPositionLow and nPositionHigh fields MUST be
95
   * set to 0x00000000.
96
   */
97
98
0
  if (request->dwFlags & FILECONTENTS_SIZE)
99
0
  {
100
0
    if (request->cbRequested != sizeof(UINT64))
101
0
    {
102
0
      WLog_ERR(TAG, "cbRequested must be %" PRIu32 ", got %" PRIu32 "", sizeof(UINT64),
103
0
               request->cbRequested);
104
0
      return FALSE;
105
0
    }
106
107
0
    if (request->nPositionHigh != 0 || request->nPositionLow != 0)
108
0
    {
109
0
      WLog_ERR(TAG, "nPositionHigh and nPositionLow must be set to 0");
110
0
      return FALSE;
111
0
    }
112
0
  }
113
114
0
  return TRUE;
115
0
}
116
117
wStream* cliprdr_packet_new(UINT16 msgType, UINT16 msgFlags, UINT32 dataLen)
118
0
{
119
0
  wStream* s = NULL;
120
0
  s = Stream_New(NULL, dataLen + 8);
121
122
0
  if (!s)
123
0
  {
124
0
    WLog_ERR(TAG, "Stream_New failed!");
125
0
    return NULL;
126
0
  }
127
128
0
  Stream_Write_UINT16(s, msgType);
129
0
  Stream_Write_UINT16(s, msgFlags);
130
  /* Write actual length after the entire packet has been constructed. */
131
0
  Stream_Write_UINT32(s, 0);
132
0
  return s;
133
0
}
134
135
static void cliprdr_write_file_contents_request(wStream* s,
136
                                                const CLIPRDR_FILE_CONTENTS_REQUEST* request)
137
0
{
138
0
  Stream_Write_UINT32(s, request->streamId);      /* streamId (4 bytes) */
139
0
  Stream_Write_UINT32(s, request->listIndex);     /* listIndex (4 bytes) */
140
0
  Stream_Write_UINT32(s, request->dwFlags);       /* dwFlags (4 bytes) */
141
0
  Stream_Write_UINT32(s, request->nPositionLow);  /* nPositionLow (4 bytes) */
142
0
  Stream_Write_UINT32(s, request->nPositionHigh); /* nPositionHigh (4 bytes) */
143
0
  Stream_Write_UINT32(s, request->cbRequested);   /* cbRequested (4 bytes) */
144
145
0
  if (request->haveClipDataId)
146
0
    Stream_Write_UINT32(s, request->clipDataId); /* clipDataId (4 bytes) */
147
0
}
148
149
static INLINE void cliprdr_write_lock_unlock_clipdata(wStream* s, UINT32 clipDataId)
150
0
{
151
0
  Stream_Write_UINT32(s, clipDataId);
152
0
}
153
154
static void cliprdr_write_lock_clipdata(wStream* s,
155
                                        const CLIPRDR_LOCK_CLIPBOARD_DATA* lockClipboardData)
156
0
{
157
0
  cliprdr_write_lock_unlock_clipdata(s, lockClipboardData->clipDataId);
158
0
}
159
160
static void cliprdr_write_unlock_clipdata(wStream* s,
161
                                          const CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockClipboardData)
162
0
{
163
0
  cliprdr_write_lock_unlock_clipdata(s, unlockClipboardData->clipDataId);
164
0
}
165
166
static void cliprdr_write_file_contents_response(wStream* s,
167
                                                 const CLIPRDR_FILE_CONTENTS_RESPONSE* response)
168
0
{
169
0
  Stream_Write_UINT32(s, response->streamId); /* streamId (4 bytes) */
170
0
  Stream_Write(s, response->requestedData, response->cbRequested);
171
0
}
172
173
wStream* cliprdr_packet_lock_clipdata_new(const CLIPRDR_LOCK_CLIPBOARD_DATA* lockClipboardData)
174
0
{
175
0
  wStream* s = NULL;
176
177
0
  if (!lockClipboardData)
178
0
    return NULL;
179
180
0
  s = cliprdr_packet_new(CB_LOCK_CLIPDATA, 0, 4);
181
182
0
  if (!s)
183
0
    return NULL;
184
185
0
  cliprdr_write_lock_clipdata(s, lockClipboardData);
186
0
  return s;
187
0
}
188
189
wStream*
190
cliprdr_packet_unlock_clipdata_new(const CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockClipboardData)
191
0
{
192
0
  wStream* s = NULL;
193
194
0
  if (!unlockClipboardData)
195
0
    return NULL;
196
197
0
  s = cliprdr_packet_new(CB_UNLOCK_CLIPDATA, 0, 4);
198
199
0
  if (!s)
200
0
    return NULL;
201
202
0
  cliprdr_write_unlock_clipdata(s, unlockClipboardData);
203
0
  return s;
204
0
}
205
206
wStream* cliprdr_packet_file_contents_request_new(const CLIPRDR_FILE_CONTENTS_REQUEST* request)
207
0
{
208
0
  wStream* s = NULL;
209
210
0
  if (!request)
211
0
    return NULL;
212
213
0
  s = cliprdr_packet_new(CB_FILECONTENTS_REQUEST, 0, 28);
214
215
0
  if (!s)
216
0
    return NULL;
217
218
0
  cliprdr_write_file_contents_request(s, request);
219
0
  return s;
220
0
}
221
222
wStream* cliprdr_packet_file_contents_response_new(const CLIPRDR_FILE_CONTENTS_RESPONSE* response)
223
0
{
224
0
  wStream* s = NULL;
225
226
0
  if (!response)
227
0
    return NULL;
228
229
0
  s = cliprdr_packet_new(CB_FILECONTENTS_RESPONSE, response->common.msgFlags,
230
0
                         4 + response->cbRequested);
231
232
0
  if (!s)
233
0
    return NULL;
234
235
0
  cliprdr_write_file_contents_response(s, response);
236
0
  return s;
237
0
}
238
239
wStream* cliprdr_packet_format_list_new(const CLIPRDR_FORMAT_LIST* formatList,
240
                                        BOOL useLongFormatNames, BOOL useAsciiNames)
241
0
{
242
0
  WINPR_ASSERT(formatList);
243
244
0
  if (formatList->common.msgType != CB_FORMAT_LIST)
245
0
    WLog_WARN(TAG, "called with invalid type %08" PRIx32, formatList->common.msgType);
246
247
0
  if (useLongFormatNames && useAsciiNames)
248
0
    WLog_WARN(TAG, "called with invalid arguments useLongFormatNames=true && "
249
0
                   "useAsciiNames=true. useAsciiNames requires "
250
0
                   "useLongFormatNames=false, ignoring argument.");
251
252
0
  const UINT32 length = formatList->numFormats * 36;
253
0
  const size_t formatNameCharSize =
254
0
      (useLongFormatNames || !useAsciiNames) ? sizeof(WCHAR) : sizeof(CHAR);
255
256
0
  wStream* s = cliprdr_packet_new(CB_FORMAT_LIST, 0, length);
257
0
  if (!s)
258
0
  {
259
0
    WLog_ERR(TAG, "cliprdr_packet_new failed!");
260
0
    return NULL;
261
0
  }
262
263
0
  for (UINT32 index = 0; index < formatList->numFormats; index++)
264
0
  {
265
0
    const CLIPRDR_FORMAT* format = &(formatList->formats[index]);
266
267
0
    const char* szFormatName = format->formatName;
268
0
    size_t formatNameLength = 0;
269
0
    if (szFormatName)
270
0
      formatNameLength = strlen(szFormatName);
271
272
0
    size_t formatNameMaxLength = formatNameLength + 1; /* Ensure '\0' termination in output */
273
0
    if (!Stream_EnsureRemainingCapacity(s,
274
0
                                        4 + MAX(32, formatNameMaxLength * formatNameCharSize)))
275
0
      goto fail;
276
277
0
    Stream_Write_UINT32(s, format->formatId); /* formatId (4 bytes) */
278
279
0
    if (!useLongFormatNames)
280
0
    {
281
0
      formatNameMaxLength = useAsciiNames ? 32 : 16;
282
0
      formatNameLength = MIN(formatNameMaxLength - 1, formatNameLength);
283
0
    }
284
285
0
    if (szFormatName && (formatNameLength > 0))
286
0
    {
287
0
      if (useAsciiNames)
288
0
      {
289
0
        Stream_Write(s, szFormatName, formatNameLength);
290
0
        Stream_Zero(s, formatNameMaxLength - formatNameLength);
291
0
      }
292
0
      else
293
0
      {
294
0
        if (Stream_Write_UTF16_String_From_UTF8(s, formatNameMaxLength, szFormatName,
295
0
                                                formatNameLength, TRUE) < 0)
296
0
          goto fail;
297
0
      }
298
0
    }
299
0
    else
300
0
      Stream_Zero(s, formatNameMaxLength * formatNameCharSize);
301
0
  }
302
303
0
  return s;
304
305
0
fail:
306
0
  Stream_Free(s, TRUE);
307
0
  return NULL;
308
0
}
309
310
UINT cliprdr_read_unlock_clipdata(wStream* s, CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockClipboardData)
311
0
{
312
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
313
0
    return ERROR_INVALID_DATA;
314
315
0
  Stream_Read_UINT32(s, unlockClipboardData->clipDataId); /* clipDataId (4 bytes) */
316
0
  return CHANNEL_RC_OK;
317
0
}
318
319
UINT cliprdr_read_format_data_request(wStream* s, CLIPRDR_FORMAT_DATA_REQUEST* request)
320
0
{
321
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
322
0
    return ERROR_INVALID_DATA;
323
324
0
  Stream_Read_UINT32(s, request->requestedFormatId); /* requestedFormatId (4 bytes) */
325
0
  return CHANNEL_RC_OK;
326
0
}
327
328
UINT cliprdr_read_format_data_response(wStream* s, CLIPRDR_FORMAT_DATA_RESPONSE* response)
329
0
{
330
0
  response->requestedFormatData = NULL;
331
332
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, response->common.dataLen))
333
0
    return ERROR_INVALID_DATA;
334
335
0
  if (response->common.dataLen)
336
0
    response->requestedFormatData = Stream_ConstPointer(s);
337
338
0
  return CHANNEL_RC_OK;
339
0
}
340
341
UINT cliprdr_read_file_contents_request(wStream* s, CLIPRDR_FILE_CONTENTS_REQUEST* request)
342
0
{
343
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 24))
344
0
    return ERROR_INVALID_DATA;
345
346
0
  request->haveClipDataId = FALSE;
347
0
  Stream_Read_UINT32(s, request->streamId);      /* streamId (4 bytes) */
348
0
  Stream_Read_UINT32(s, request->listIndex);     /* listIndex (4 bytes) */
349
0
  Stream_Read_UINT32(s, request->dwFlags);       /* dwFlags (4 bytes) */
350
0
  Stream_Read_UINT32(s, request->nPositionLow);  /* nPositionLow (4 bytes) */
351
0
  Stream_Read_UINT32(s, request->nPositionHigh); /* nPositionHigh (4 bytes) */
352
0
  Stream_Read_UINT32(s, request->cbRequested);   /* cbRequested (4 bytes) */
353
354
0
  if (Stream_GetRemainingLength(s) >= 4)
355
0
  {
356
0
    Stream_Read_UINT32(s, request->clipDataId); /* clipDataId (4 bytes) */
357
0
    request->haveClipDataId = TRUE;
358
0
  }
359
360
0
  if (!cliprdr_validate_file_contents_request(request))
361
0
    return ERROR_BAD_ARGUMENTS;
362
363
0
  return CHANNEL_RC_OK;
364
0
}
365
366
UINT cliprdr_read_file_contents_response(wStream* s, CLIPRDR_FILE_CONTENTS_RESPONSE* response)
367
0
{
368
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
369
0
    return ERROR_INVALID_DATA;
370
371
0
  Stream_Read_UINT32(s, response->streamId);   /* streamId (4 bytes) */
372
0
  response->requestedData = Stream_ConstPointer(s); /* requestedFileContentsData */
373
374
0
  WINPR_ASSERT(response->common.dataLen >= 4);
375
0
  response->cbRequested = response->common.dataLen - 4;
376
0
  return CHANNEL_RC_OK;
377
0
}
378
379
UINT cliprdr_read_format_list(wStream* s, CLIPRDR_FORMAT_LIST* formatList, BOOL useLongFormatNames)
380
0
{
381
0
  UINT32 index = 0;
382
0
  size_t formatNameLength = 0;
383
0
  const char* szFormatName = NULL;
384
0
  const WCHAR* wszFormatName = NULL;
385
0
  wStream sub1buffer = { 0 };
386
0
  CLIPRDR_FORMAT* formats = NULL;
387
0
  UINT error = ERROR_INTERNAL_ERROR;
388
389
0
  const BOOL asciiNames = (formatList->common.msgFlags & CB_ASCII_NAMES) ? TRUE : FALSE;
390
391
0
  index = 0;
392
  /* empty format list */
393
0
  formatList->formats = NULL;
394
0
  formatList->numFormats = 0;
395
396
0
  wStream* sub1 =
397
0
      Stream_StaticConstInit(&sub1buffer, Stream_ConstPointer(s), formatList->common.dataLen);
398
0
  if (!Stream_SafeSeek(s, formatList->common.dataLen))
399
0
    return ERROR_INVALID_DATA;
400
401
0
  if (!formatList->common.dataLen)
402
0
  {
403
0
  }
404
0
  else if (!useLongFormatNames)
405
0
  {
406
0
    const size_t cap = Stream_Capacity(sub1);
407
0
    formatList->numFormats = (cap / 36ULL);
408
409
0
    if ((36ULL * formatList->numFormats) != cap)
410
0
    {
411
0
      WLog_ERR(TAG, "Invalid short format list length: %" PRIuz "", cap);
412
0
      return ERROR_INTERNAL_ERROR;
413
0
    }
414
415
0
    if (formatList->numFormats)
416
0
      formats = (CLIPRDR_FORMAT*)calloc(formatList->numFormats, sizeof(CLIPRDR_FORMAT));
417
418
0
    if (!formats)
419
0
    {
420
0
      WLog_ERR(TAG, "calloc failed!");
421
0
      return CHANNEL_RC_NO_MEMORY;
422
0
    }
423
424
0
    formatList->formats = formats;
425
426
0
    while (Stream_GetRemainingLength(sub1) >= 4)
427
0
    {
428
0
      CLIPRDR_FORMAT* format = &formats[index];
429
430
0
      Stream_Read_UINT32(sub1, format->formatId); /* formatId (4 bytes) */
431
432
      /* According to MS-RDPECLIP 2.2.3.1.1.1 formatName is "a 32-byte block containing
433
       * the *null-terminated* name assigned to the Clipboard Format: (32 ASCII 8 characters
434
       * or 16 Unicode characters)"
435
       * However, both Windows RDSH and mstsc violate this specs as seen in the following
436
       * example of a transferred short format name string: [R.i.c.h. .T.e.x.t. .F.o.r.m.a.t.]
437
       * These are 16 unicode charaters - *without* terminating null !
438
       */
439
440
0
      szFormatName = Stream_ConstPointer(sub1);
441
0
      wszFormatName = Stream_ConstPointer(sub1);
442
0
      if (!Stream_SafeSeek(sub1, 32))
443
0
        goto error_out;
444
445
0
      free(format->formatName);
446
0
      format->formatName = NULL;
447
448
0
      if (asciiNames)
449
0
      {
450
0
        if (szFormatName[0])
451
0
        {
452
          /* ensure null termination */
453
0
          format->formatName = strndup(szFormatName, 31);
454
0
          if (!format->formatName)
455
0
          {
456
0
            WLog_ERR(TAG, "malloc failed!");
457
0
            error = CHANNEL_RC_NO_MEMORY;
458
0
            goto error_out;
459
0
          }
460
0
        }
461
0
      }
462
0
      else
463
0
      {
464
0
        if (wszFormatName[0])
465
0
        {
466
0
          format->formatName = ConvertWCharNToUtf8Alloc(wszFormatName, 16, NULL);
467
0
          if (!format->formatName)
468
0
            goto error_out;
469
0
        }
470
0
      }
471
472
0
      index++;
473
0
    }
474
0
  }
475
0
  else
476
0
  {
477
0
    wStream sub2buffer = sub1buffer;
478
0
    wStream* sub2 = &sub2buffer;
479
480
0
    while (Stream_GetRemainingLength(sub1) > 0)
481
0
    {
482
0
      size_t rest = 0;
483
0
      if (!Stream_SafeSeek(sub1, 4)) /* formatId (4 bytes) */
484
0
        goto error_out;
485
486
0
      wszFormatName = Stream_ConstPointer(sub1);
487
0
      rest = Stream_GetRemainingLength(sub1);
488
0
      formatNameLength = _wcsnlen(wszFormatName, rest / sizeof(WCHAR));
489
490
0
      if (!Stream_SafeSeek(sub1, (formatNameLength + 1) * sizeof(WCHAR)))
491
0
        goto error_out;
492
0
      formatList->numFormats++;
493
0
    }
494
495
0
    if (formatList->numFormats)
496
0
      formats = (CLIPRDR_FORMAT*)calloc(formatList->numFormats, sizeof(CLIPRDR_FORMAT));
497
498
0
    if (!formats)
499
0
    {
500
0
      WLog_ERR(TAG, "calloc failed!");
501
0
      return CHANNEL_RC_NO_MEMORY;
502
0
    }
503
504
0
    formatList->formats = formats;
505
506
0
    while (Stream_GetRemainingLength(sub2) >= 4)
507
0
    {
508
0
      size_t rest = 0;
509
0
      CLIPRDR_FORMAT* format = &formats[index];
510
511
0
      Stream_Read_UINT32(sub2, format->formatId); /* formatId (4 bytes) */
512
513
0
      free(format->formatName);
514
0
      format->formatName = NULL;
515
516
0
      wszFormatName = Stream_ConstPointer(sub2);
517
0
      rest = Stream_GetRemainingLength(sub2);
518
0
      formatNameLength = _wcsnlen(wszFormatName, rest / sizeof(WCHAR));
519
0
      if (!Stream_SafeSeek(sub2, (formatNameLength + 1) * sizeof(WCHAR)))
520
0
        goto error_out;
521
522
0
      if (formatNameLength)
523
0
      {
524
0
        format->formatName =
525
0
            ConvertWCharNToUtf8Alloc(wszFormatName, formatNameLength, NULL);
526
0
        if (!format->formatName)
527
0
          goto error_out;
528
0
      }
529
530
0
      index++;
531
0
    }
532
0
  }
533
534
0
  return CHANNEL_RC_OK;
535
536
0
error_out:
537
0
  cliprdr_free_format_list(formatList);
538
0
  return error;
539
0
}
540
541
void cliprdr_free_format_list(CLIPRDR_FORMAT_LIST* formatList)
542
0
{
543
0
  if (formatList == NULL)
544
0
    return;
545
546
0
  if (formatList->formats)
547
0
  {
548
0
    for (UINT32 index = 0; index < formatList->numFormats; index++)
549
0
    {
550
0
      free(formatList->formats[index].formatName);
551
0
    }
552
553
0
    free(formatList->formats);
554
0
    formatList->formats = NULL;
555
0
    formatList->numFormats = 0;
556
0
  }
557
0
}