1 /* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3    Copyright (C) 2020 Red Hat, Inc.
4 
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 */
18 #include "agent.h"
19 
20 #ifdef _WIN32
21 // Windows is always little endian
22 #  define FIX_ENDIAN16(x) (x) = (x)
23 #  define FIX_ENDIAN32(x) (x) = (x)
24 #  define FIX_ENDIAN64(x) (x) = (x)
25 #else
26 #  include <glib.h>
27 #  define FIX_ENDIAN16(x) (x) = GUINT16_FROM_LE(x)
28 #  define FIX_ENDIAN32(x) (x) = GUINT32_FROM_LE(x)
29 #  define FIX_ENDIAN64(x) (x) = GUINT64_FROM_LE(x)
30 #endif
31 
32 #include <spice/start-packed.h>
33 typedef struct SPICE_ATTR_PACKED {
34     uint16_t v;
35 } uint16_unaligned_t;
36 
37 typedef struct SPICE_ATTR_PACKED {
38     uint32_t v;
39 } uint32_unaligned_t;
40 
41 typedef struct SPICE_ATTR_PACKED {
42     uint64_t v;
43 } uint64_unaligned_t;
44 #include <spice/end-packed.h>
45 
46 static const int agent_message_min_size[] =
47 {
48     -1, /* Does not exist */
49     sizeof(VDAgentMouseState), /* VD_AGENT_MOUSE_STATE */
50     sizeof(VDAgentMonitorsConfig), /* VD_AGENT_MONITORS_CONFIG */
51     sizeof(VDAgentReply), /* VD_AGENT_REPLY */
52     sizeof(VDAgentClipboard), /* VD_AGENT_CLIPBOARD */
53     sizeof(VDAgentDisplayConfig), /* VD_AGENT_DISPLAY_CONFIG */
54     sizeof(VDAgentAnnounceCapabilities), /* VD_AGENT_ANNOUNCE_CAPABILITIES */
55     sizeof(VDAgentClipboardGrab), /* VD_AGENT_CLIPBOARD_GRAB */
56     sizeof(VDAgentClipboardRequest), /* VD_AGENT_CLIPBOARD_REQUEST */
57     sizeof(VDAgentClipboardRelease), /* VD_AGENT_CLIPBOARD_RELEASE */
58     sizeof(VDAgentFileXferStartMessage), /* VD_AGENT_FILE_XFER_START */
59     sizeof(VDAgentFileXferStatusMessage), /* VD_AGENT_FILE_XFER_STATUS */
60     sizeof(VDAgentFileXferDataMessage), /* VD_AGENT_FILE_XFER_DATA */
61     0, /* VD_AGENT_CLIENT_DISCONNECTED */
62     sizeof(VDAgentMaxClipboard), /* VD_AGENT_MAX_CLIPBOARD */
63     sizeof(VDAgentAudioVolumeSync), /* VD_AGENT_AUDIO_VOLUME_SYNC */
64     sizeof(VDAgentGraphicsDeviceInfo), /* VD_AGENT_GRAPHICS_DEVICE_INFO */
65 };
66 
67 static AgentCheckResult
agent_message_check_size(const VDAgentMessage * message_header,const uint32_t * capabilities,uint32_t capabilities_size)68 agent_message_check_size(const VDAgentMessage *message_header,
69                          const uint32_t *capabilities, uint32_t capabilities_size)
70 {
71     if (message_header->protocol != VD_AGENT_PROTOCOL) {
72         return AGENT_CHECK_WRONG_PROTOCOL_VERSION;
73     }
74 
75     if (message_header->type >= SPICE_N_ELEMENTS(agent_message_min_size) ||
76         agent_message_min_size[message_header->type] < 0) {
77         return AGENT_CHECK_UNKNOWN_MESSAGE;
78     }
79 
80     uint32_t min_size = agent_message_min_size[message_header->type];
81 
82     if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
83                                 VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
84         switch (message_header->type) {
85         case VD_AGENT_CLIPBOARD_GRAB:
86         case VD_AGENT_CLIPBOARD_REQUEST:
87         case VD_AGENT_CLIPBOARD:
88         case VD_AGENT_CLIPBOARD_RELEASE:
89             min_size += 4;
90         }
91     }
92 
93     if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
94                                 VD_AGENT_CAP_CLIPBOARD_GRAB_SERIAL)
95         && message_header->type == VD_AGENT_CLIPBOARD_GRAB) {
96         min_size += 4;
97     }
98 
99     switch (message_header->type) {
100     case VD_AGENT_MONITORS_CONFIG:
101     case VD_AGENT_FILE_XFER_START:
102     case VD_AGENT_FILE_XFER_DATA:
103     case VD_AGENT_CLIPBOARD:
104     case VD_AGENT_CLIPBOARD_GRAB:
105     case VD_AGENT_AUDIO_VOLUME_SYNC:
106     case VD_AGENT_ANNOUNCE_CAPABILITIES:
107     case VD_AGENT_GRAPHICS_DEVICE_INFO:
108     case VD_AGENT_FILE_XFER_STATUS:
109         if (message_header->size < min_size) {
110             return AGENT_CHECK_INVALID_SIZE;
111         }
112         break;
113     case VD_AGENT_MOUSE_STATE:
114     case VD_AGENT_DISPLAY_CONFIG:
115     case VD_AGENT_REPLY:
116     case VD_AGENT_CLIPBOARD_REQUEST:
117     case VD_AGENT_CLIPBOARD_RELEASE:
118     case VD_AGENT_MAX_CLIPBOARD:
119     case VD_AGENT_CLIENT_DISCONNECTED:
120         if (message_header->size != min_size) {
121             return AGENT_CHECK_INVALID_SIZE;
122         }
123         break;
124     default:
125         return AGENT_CHECK_UNKNOWN_MESSAGE;
126     }
127     return AGENT_CHECK_NO_ERROR;
128 }
129 
uint16_from_le(uint8_t * _msg,uint32_t size,uint32_t offset)130 static void uint16_from_le(uint8_t *_msg, uint32_t size, uint32_t offset)
131 {
132     uint32_t i;
133     uint16_unaligned_t *msg = (uint16_unaligned_t *)(_msg + offset);
134 
135     /* size % 2 should be 0 - extra bytes are ignored */
136     for (i = 0; i < size / 2; i++) {
137         FIX_ENDIAN16(msg[i].v);
138     }
139 }
140 
uint32_from_le(uint8_t * _msg,uint32_t size,uint32_t offset)141 static void uint32_from_le(uint8_t *_msg, uint32_t size, uint32_t offset)
142 {
143     uint32_t i;
144     uint32_unaligned_t *msg = (uint32_unaligned_t *)(_msg + offset);
145 
146     /* size % 4 should be 0 - extra bytes are ignored */
147     for (i = 0; i < size / 4; i++) {
148         FIX_ENDIAN32(msg[i].v);
149     }
150 }
151 
152 static void
agent_message_clipboard_from_le(const VDAgentMessage * message_header,uint8_t * data,const uint32_t * capabilities,uint32_t capabilities_size)153 agent_message_clipboard_from_le(const VDAgentMessage *message_header, uint8_t *data,
154                                 const uint32_t *capabilities, uint32_t capabilities_size)
155 {
156     size_t min_size = agent_message_min_size[message_header->type];
157     uint32_unaligned_t *data_type = (uint32_unaligned_t *) data;
158 
159     if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
160                                 VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
161         min_size += 4;
162         data_type++;
163     }
164 
165     switch (message_header->type) {
166     case VD_AGENT_CLIPBOARD_REQUEST:
167     case VD_AGENT_CLIPBOARD:
168         FIX_ENDIAN32(data_type->v);
169         break;
170     case VD_AGENT_CLIPBOARD_GRAB:
171         uint32_from_le(data, message_header->size - min_size, min_size);
172         break;
173     case VD_AGENT_CLIPBOARD_RELEASE:
174         // empty
175         break;
176     }
177 }
178 
179 static void
agent_message_file_xfer_from_le(const VDAgentMessage * message_header,uint8_t * data)180 agent_message_file_xfer_from_le(const VDAgentMessage *message_header, uint8_t *data)
181 {
182     uint32_unaligned_t *id = (uint32_unaligned_t *)data;
183     FIX_ENDIAN32(id->v);
184     id++; // result
185 
186     switch (message_header->type) {
187     case VD_AGENT_FILE_XFER_DATA: {
188         VDAgentFileXferDataMessage *msg = (VDAgentFileXferDataMessage *) data;
189         FIX_ENDIAN64(msg->size);
190         break;
191     }
192     case VD_AGENT_FILE_XFER_STATUS: {
193         VDAgentFileXferStatusMessage *msg = (VDAgentFileXferStatusMessage *) data;
194         FIX_ENDIAN32(msg->result);
195         // from client/server we don't expect any detail
196         switch (msg->result) {
197         case VD_AGENT_FILE_XFER_STATUS_NOT_ENOUGH_SPACE:
198             if (message_header->size >= sizeof(VDAgentFileXferStatusMessage) +
199                 sizeof(VDAgentFileXferStatusNotEnoughSpace)) {
200                 VDAgentFileXferStatusNotEnoughSpace *err =
201                     (VDAgentFileXferStatusNotEnoughSpace*) msg->data;
202                 FIX_ENDIAN64(err->disk_free_space);
203             }
204             break;
205         case VD_AGENT_FILE_XFER_STATUS_ERROR:
206             if (message_header->size >= sizeof(VDAgentFileXferStatusMessage) +
207                 sizeof(VDAgentFileXferStatusError)) {
208                 VDAgentFileXferStatusError *err =
209                     (VDAgentFileXferStatusError *) msg->data;
210                 FIX_ENDIAN32(err->error_code);
211             }
212             break;
213         }
214         break;
215     }
216     }
217 }
218 
219 static AgentCheckResult
agent_message_graphics_device_info_check_from_le(const VDAgentMessage * message_header,uint8_t * data)220 agent_message_graphics_device_info_check_from_le(const VDAgentMessage *message_header,
221                                                  uint8_t *data)
222 {
223     uint8_t *const end = data + message_header->size;
224     uint32_unaligned_t *u32 = (uint32_unaligned_t *) data;
225     FIX_ENDIAN32(u32->v);
226     const uint32_t count = u32->v;
227     data += 4;
228 
229     for (size_t i = 0; i < count; ++i) {
230         if ((size_t) (end - data) < sizeof(VDAgentDeviceDisplayInfo)) {
231             return AGENT_CHECK_TRUNCATED;
232         }
233         uint32_from_le(data, sizeof(VDAgentDeviceDisplayInfo), 0);
234         VDAgentDeviceDisplayInfo *info = (VDAgentDeviceDisplayInfo *) data;
235         data += sizeof(VDAgentDeviceDisplayInfo);
236         if (!info->device_address_len) {
237             return AGENT_CHECK_INVALID_DATA;
238         }
239         if ((size_t) (end - data) < info->device_address_len) {
240             return AGENT_CHECK_TRUNCATED;
241         }
242         info->device_address[info->device_address_len - 1] = 0;
243         data += info->device_address_len;
244     }
245     return AGENT_CHECK_NO_ERROR;
246 }
247 
248 static AgentCheckResult
agent_message_monitors_config_from_le(const VDAgentMessage * message_header,uint8_t * message)249 agent_message_monitors_config_from_le(const VDAgentMessage *message_header, uint8_t *message)
250 {
251     uint32_from_le(message, sizeof(VDAgentMonitorsConfig), 0);
252     VDAgentMonitorsConfig *vdata = (VDAgentMonitorsConfig*) message;
253     vdata->flags &= VD_AGENT_CONFIG_MONITORS_FLAG_USE_POS|
254         VD_AGENT_CONFIG_MONITORS_FLAG_PHYSICAL_SIZE;
255     size_t element_size = sizeof(vdata->monitors[0]);
256     if ((vdata->flags & VD_AGENT_CONFIG_MONITORS_FLAG_PHYSICAL_SIZE) != 0) {
257         element_size += sizeof(VDAgentMonitorMM);
258     }
259     const size_t max_monitors =
260         (message_header->size - sizeof(*vdata)) / element_size;
261     if (vdata->num_of_monitors > max_monitors) {
262         return AGENT_CHECK_TRUNCATED;
263     }
264     uint32_from_le(message, sizeof(vdata->monitors[0]) * vdata->num_of_monitors,
265                    sizeof(*vdata));
266     if ((vdata->flags & VD_AGENT_CONFIG_MONITORS_FLAG_PHYSICAL_SIZE) != 0) {
267         uint16_from_le(message, sizeof(VDAgentMonitorMM) * vdata->num_of_monitors,
268                        sizeof(*vdata) + sizeof(vdata->monitors[0]) * vdata->num_of_monitors);
269     }
270     return AGENT_CHECK_NO_ERROR;
271 }
272 
273 AgentCheckResult
agent_check_message(const VDAgentMessage * message_header,uint8_t * message,const uint32_t * capabilities,uint32_t capabilities_size)274 agent_check_message(const VDAgentMessage *message_header, uint8_t *message,
275                     const uint32_t *capabilities, uint32_t capabilities_size)
276 {
277     AgentCheckResult res;
278 
279     res = agent_message_check_size(message_header, capabilities, capabilities_size);
280     if (res != AGENT_CHECK_NO_ERROR) {
281         return res;
282     }
283 
284     switch (message_header->type) {
285     case VD_AGENT_MOUSE_STATE:
286         uint32_from_le(message, 3 * sizeof(uint32_t), 0);
287         break;
288     case VD_AGENT_REPLY:
289     case VD_AGENT_DISPLAY_CONFIG:
290     case VD_AGENT_MAX_CLIPBOARD:
291     case VD_AGENT_ANNOUNCE_CAPABILITIES:
292         uint32_from_le(message, message_header->size, 0);
293         break;
294     case VD_AGENT_MONITORS_CONFIG:
295         return agent_message_monitors_config_from_le(message_header, message);
296 
297     case VD_AGENT_CLIPBOARD:
298     case VD_AGENT_CLIPBOARD_GRAB:
299     case VD_AGENT_CLIPBOARD_REQUEST:
300     case VD_AGENT_CLIPBOARD_RELEASE:
301         agent_message_clipboard_from_le(message_header, message,
302                                         capabilities, capabilities_size);
303         break;
304     case VD_AGENT_FILE_XFER_START:
305     case VD_AGENT_FILE_XFER_STATUS:
306     case VD_AGENT_FILE_XFER_DATA:
307         agent_message_file_xfer_from_le(message_header, message);
308         break;
309     case VD_AGENT_CLIENT_DISCONNECTED:
310         break;
311     case VD_AGENT_GRAPHICS_DEVICE_INFO:
312         return agent_message_graphics_device_info_check_from_le(message_header, message);
313 
314     case VD_AGENT_AUDIO_VOLUME_SYNC: {
315         VDAgentAudioVolumeSync *vdata = (VDAgentAudioVolumeSync *)message;
316         const size_t max_channels =
317             (message_header->size - sizeof(*vdata)) / sizeof(vdata->volume[0]);
318         if (vdata->nchannels > max_channels) {
319             return AGENT_CHECK_TRUNCATED;
320         }
321         uint16_from_le(message, message_header->size - sizeof(*vdata), sizeof(*vdata));
322         break;
323     }
324     default:
325         return AGENT_CHECK_UNKNOWN_MESSAGE;
326     }
327     return AGENT_CHECK_NO_ERROR;
328 }
329 
330 void
agent_prepare_filexfer_status(AgentFileXferStatusMessageFull * status,size_t * status_size,const uint32_t * capabilities,uint32_t capabilities_size)331 agent_prepare_filexfer_status(AgentFileXferStatusMessageFull *status, size_t *status_size,
332                               const uint32_t *capabilities, uint32_t capabilities_size)
333 {
334     if (*status_size < sizeof(status->common)) {
335         *status_size = sizeof(status->common);
336     }
337 
338     // if there are details but no cap for detail remove it
339     if (!VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
340                                  VD_AGENT_CAP_FILE_XFER_DETAILED_ERRORS)) {
341         *status_size = sizeof(status->common);
342 
343         // if detail cap is not provided and error > threshold set to error
344         if (status->common.result >= VD_AGENT_FILE_XFER_STATUS_NOT_ENOUGH_SPACE) {
345             status->common.result = VD_AGENT_FILE_XFER_STATUS_ERROR;
346         }
347     }
348 
349     // fix endian
350     switch (status->common.result) {
351     case VD_AGENT_FILE_XFER_STATUS_NOT_ENOUGH_SPACE:
352         FIX_ENDIAN64(status->not_enough_space.disk_free_space);
353         break;
354     case VD_AGENT_FILE_XFER_STATUS_ERROR:
355         FIX_ENDIAN32(status->error.error_code);
356         break;
357     }
358     // header should be done last
359     FIX_ENDIAN32(status->common.id);
360     FIX_ENDIAN32(status->common.result);
361 }
362