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