FreeRDP
cliprdr_utils.c
1 
22 #include <winpr/stream.h>
23 #include <freerdp/utils/cliprdr_utils.h>
24 #include <freerdp/channels/cliprdr.h>
25 
26 #include <freerdp/log.h>
27 #define TAG FREERDP_TAG("utils." CLIPRDR_SVC_CHANNEL_NAME)
28 
29 #define CLIPRDR_FILEDESCRIPTOR_SIZE (4 + 32 + 4 + 16 + 8 + 8 + 520)
30 #define CLIPRDR_MAX_FILE_SIZE (2U * 1024 * 1024 * 1024)
31 
32 static UINT64 filetime_to_uint64(FILETIME value)
33 {
34  UINT64 converted = 0;
35  converted |= (UINT32)value.dwHighDateTime;
36  converted <<= 32;
37  converted |= (UINT32)value.dwLowDateTime;
38  return converted;
39 }
40 
41 static FILETIME uint64_to_filetime(UINT64 value)
42 {
43  FILETIME converted;
44  converted.dwLowDateTime = (UINT32)(value >> 0);
45  converted.dwHighDateTime = (UINT32)(value >> 32);
46  return converted;
47 }
48 
61 UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length,
62  FILEDESCRIPTORW** file_descriptor_array, UINT32* file_descriptor_count)
63 {
64  UINT result = NO_ERROR;
65  UINT32 count = 0;
66  wStream sbuffer;
67  wStream* s = NULL;
68 
69  if (!format_data || !file_descriptor_array || !file_descriptor_count)
70  return ERROR_BAD_ARGUMENTS;
71 
72  s = Stream_StaticConstInit(&sbuffer, format_data, format_data_length);
73  if (!s)
74  return ERROR_NOT_ENOUGH_MEMORY;
75 
76  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
77  {
78  result = ERROR_INCORRECT_SIZE;
79  goto out;
80  }
81 
82  Stream_Read_UINT32(s, count); /* cItems (4 bytes) */
83 
84  if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, count, CLIPRDR_FILEDESCRIPTOR_SIZE))
85  {
86  result = ERROR_INCORRECT_SIZE;
87  goto out;
88  }
89 
90  *file_descriptor_count = count;
91  *file_descriptor_array = calloc(count, sizeof(FILEDESCRIPTORW));
92  if (!*file_descriptor_array)
93  {
94  result = ERROR_NOT_ENOUGH_MEMORY;
95  goto out;
96  }
97 
98  for (UINT32 i = 0; i < count; i++)
99  {
100  FILEDESCRIPTORW* file = &((*file_descriptor_array)[i]);
101 
102  if (!cliprdr_read_filedescriptor(s, file))
103  goto out;
104  }
105 
106  if (Stream_GetRemainingLength(s) > 0)
107  WLog_WARN(TAG, "packed file list has %" PRIuz " excess bytes",
108  Stream_GetRemainingLength(s));
109 out:
110 
111  return result;
112 }
113 
114 BOOL cliprdr_read_filedescriptor(wStream* s, FILEDESCRIPTORW* file)
115 {
116  UINT64 tmp = 0;
117  WINPR_ASSERT(file);
118 
119  if (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(FILEDESCRIPTORW)))
120  return FALSE;
121 
122  Stream_Read_UINT32(s, file->dwFlags); /* flags (4 bytes) */
123  Stream_Read_UINT32(s, file->clsid.Data1);
124  Stream_Read_UINT16(s, file->clsid.Data2);
125  Stream_Read_UINT16(s, file->clsid.Data3);
126  Stream_Read(s, &file->clsid.Data4, sizeof(file->clsid.Data4));
127  Stream_Read_INT32(s, file->sizel.cx);
128  Stream_Read_INT32(s, file->sizel.cy);
129  Stream_Read_INT32(s, file->pointl.x);
130  Stream_Read_INT32(s, file->pointl.y);
131  Stream_Read_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
132  Stream_Read_UINT64(s, tmp); /* ftCreationTime (8 bytes) */
133  file->ftCreationTime = uint64_to_filetime(tmp);
134  Stream_Read_UINT64(s, tmp); /* ftLastAccessTime (8 bytes) */
135  file->ftLastAccessTime = uint64_to_filetime(tmp);
136  Stream_Read_UINT64(s, tmp); /* lastWriteTime (8 bytes) */
137  file->ftLastWriteTime = uint64_to_filetime(tmp);
138  Stream_Read_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
139  Stream_Read_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
140  Stream_Read_UTF16_String(s, file->cFileName,
141  ARRAYSIZE(file->cFileName)); /* cFileName (520 bytes) */
142  return TRUE;
143 }
144 
145 BOOL cliprdr_write_filedescriptor(wStream* s, const FILEDESCRIPTORW* file)
146 {
147  WINPR_ASSERT(file);
148 
149  if (!Stream_EnsureRemainingCapacity(s, sizeof(FILEDESCRIPTORW)))
150  return FALSE;
151 
152  Stream_Write_UINT32(s, file->dwFlags); /* flags (4 bytes) */
153 
154  Stream_Write_UINT32(s, file->clsid.Data1);
155  Stream_Write_UINT16(s, file->clsid.Data2);
156  Stream_Write_UINT16(s, file->clsid.Data3);
157  Stream_Write(s, &file->clsid.Data4, sizeof(file->clsid.Data4));
158  Stream_Write_INT32(s, file->sizel.cx);
159  Stream_Write_INT32(s, file->sizel.cy);
160  Stream_Write_INT32(s, file->pointl.x);
161  Stream_Write_INT32(s, file->pointl.y);
162  Stream_Write_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
163  Stream_Write_UINT64(s, filetime_to_uint64(file->ftCreationTime));
164  Stream_Write_UINT64(s, filetime_to_uint64(file->ftLastAccessTime));
165  Stream_Write_UINT64(s, filetime_to_uint64(file->ftLastWriteTime)); /* lastWriteTime (8 bytes) */
166  Stream_Write_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
167  Stream_Write_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
168  Stream_Write_UTF16_String(s, file->cFileName,
169  ARRAYSIZE(file->cFileName)); /* cFileName (520 bytes) */
170  return TRUE;
171 }
172 
185 UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array,
186  UINT32 file_descriptor_count, BYTE** format_data,
187  UINT32* format_data_length)
188 {
189  return cliprdr_serialize_file_list_ex(CB_STREAM_FILECLIP_ENABLED, file_descriptor_array,
190  file_descriptor_count, format_data, format_data_length);
191 }
192 
193 UINT cliprdr_serialize_file_list_ex(UINT32 flags, const FILEDESCRIPTORW* file_descriptor_array,
194  UINT32 file_descriptor_count, BYTE** format_data,
195  UINT32* format_data_length)
196 {
197  UINT result = NO_ERROR;
198  size_t len = 0;
199  wStream* s = NULL;
200 
201  if (!file_descriptor_array || !format_data || !format_data_length)
202  return ERROR_BAD_ARGUMENTS;
203 
204  if ((flags & CB_STREAM_FILECLIP_ENABLED) == 0)
205  {
206  WLog_WARN(TAG, "No file clipboard support annouonced!");
207  return ERROR_BAD_ARGUMENTS;
208  }
209 
210  s = Stream_New(NULL, 4 + file_descriptor_count * CLIPRDR_FILEDESCRIPTOR_SIZE);
211  if (!s)
212  return ERROR_NOT_ENOUGH_MEMORY;
213 
214  Stream_Write_UINT32(s, file_descriptor_count); /* cItems (4 bytes) */
215 
216  for (UINT32 i = 0; i < file_descriptor_count; i++)
217  {
218  const FILEDESCRIPTORW* file = &file_descriptor_array[i];
219 
220  /*
221  * There is a known issue with Windows server getting stuck in
222  * an infinite loop when downloading files that are larger than
223  * 2 gigabytes. Do not allow clients to send such file lists.
224  *
225  * https://support.microsoft.com/en-us/help/2258090
226  */
227  if ((flags & CB_HUGE_FILE_SUPPORT_ENABLED) == 0)
228  {
229  if ((file->nFileSizeHigh > 0) || (file->nFileSizeLow >= CLIPRDR_MAX_FILE_SIZE))
230  {
231  WLog_ERR(TAG, "cliprdr does not support files over 2 GB");
232  result = ERROR_FILE_TOO_LARGE;
233  goto error;
234  }
235  }
236 
237  if (!cliprdr_write_filedescriptor(s, file))
238  goto error;
239  }
240 
241  Stream_SealLength(s);
242 
243  Stream_GetBuffer(s, *format_data);
244  Stream_GetLength(s, len);
245  if (len > UINT32_MAX)
246  goto error;
247 
248  *format_data_length = (UINT32)len;
249 
250  Stream_Free(s, FALSE);
251 
252  return result;
253 
254 error:
255  Stream_Free(s, TRUE);
256 
257  return result;
258 }