1 /* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3    Copyright (C) 2009 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 <config.h>
19 
20 #include <stdint.h>
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <limits.h>
24 #include <pthread.h>
25 #include <ctype.h>
26 #ifndef _WIN32
27 #include <sys/socket.h>
28 #include <sys/uio.h>
29 #include <netinet/in.h>
30 #include <netinet/tcp.h>
31 #include <arpa/inet.h>
32 #include <netdb.h>
33 #include <sys/mman.h>
34 #include <sys/un.h>
35 #else
36 #include <ws2tcpip.h>
37 #endif
38 
39 #include <openssl/bn.h>
40 #include <openssl/err.h>
41 #include <openssl/rsa.h>
42 
43 #if HAVE_SASL
44 #include <sasl/sasl.h>
45 #endif
46 
47 #include <glib.h>
48 
49 #include <spice/protocol.h>
50 #include <spice/vd_agent.h>
51 #include <spice/stats.h>
52 
53 #include <common/generated_server_marshallers.h>
54 #include <common/agent.h>
55 
56 #include "spice-wrapped.h"
57 #include "reds.h"
58 #include "agent-msg-filter.h"
59 #include "inputs-channel.h"
60 #include "main-channel.h"
61 #include "red-qxl.h"
62 #include "main-dispatcher.h"
63 #include "sound.h"
64 #include "stat.h"
65 #include "char-device.h"
66 #include "migration-protocol.h"
67 #ifdef USE_SMARTCARD
68 #include "smartcard.h"
69 #endif
70 #include "red-stream.h"
71 #include "red-client.h"
72 
73 #include "reds-private.h"
74 #include "video-encoder.h"
75 #include "red-channel-client.h"
76 #include "main-channel-client.h"
77 #include "red-client.h"
78 #include "net-utils.h"
79 #include "red-stream-device.h"
80 
81 #define REDS_MAX_STAT_NODES 100
82 
83 static void reds_client_monitors_config(RedsState *reds, VDAgentMonitorsConfig *monitors_config);
84 static gboolean reds_use_client_monitors_config(RedsState *reds);
85 static void reds_set_video_codecs(RedsState *reds, GArray *video_codecs);
86 
87 /* Debugging only variable: allow multiple client connections to the spice
88  * server */
89 #define SPICE_DEBUG_ALLOW_MC_ENV "SPICE_DEBUG_ALLOW_MC"
90 
91 #define REDS_TOKENS_TO_SEND 5
92 #define REDS_VDI_PORT_NUM_RECEIVE_BUFFS 5
93 
94 /* TODO while we can technically create more than one server in a process,
95  * the intended use is to support a single server per process */
96 static GList *servers = nullptr;
97 static pthread_mutex_t global_reds_lock = PTHREAD_MUTEX_INITIALIZER;
98 
99 /* SPICE configuration set through the public spice_server_set_xxx APIS */
100 struct RedServerConfig {
101     RedsMigSpice *mig_spice;
102 
103     int default_channel_security;
104     ChannelSecurityOptions *channels_security;
105 
106     GArray *renderers;
107 
108     int spice_port;
109     int spice_secure_port;
110     int spice_listen_socket_fd;
111     char spice_addr[256];
112     int spice_family;
113     TicketAuthentication taTicket;
114 
115     int sasl_enabled;
116 #if HAVE_SASL
117     char *sasl_appname;
118 #endif
119     char *spice_name;
120 
121     bool spice_uuid_is_set;
122     uint8_t spice_uuid[16];
123 
124     gboolean ticketing_enabled;
125     uint32_t streaming_video;
126     GArray* video_codecs;
127     SpiceImageCompression image_compression;
128     bool playback_compression;
129     spice_wan_compression_t jpeg_state;
130     spice_wan_compression_t zlib_glz_state;
131 
132     gboolean agent_mouse;
133     gboolean agent_copypaste;
134     gboolean agent_file_xfer;
135     gboolean exit_on_disconnect;
136 
137     RedSSLParameters ssl_parameters;
138 };
139 
140 
141 struct RedLinkInfo {
142     RedsState *reds;
143     RedStream *stream;
144     SpiceLinkHeader link_header;
145     SpiceLinkMess *link_mess;
146     TicketInfo tiTicketing;
147     SpiceLinkAuthMechanism auth_mechanism;
148     int skip_auth;
149 };
150 
151 struct ChannelSecurityOptions {
152     uint32_t channel_id;
153     uint32_t options;
154     ChannelSecurityOptions *next;
155 };
156 
157 struct RedVDIReadBuf final: public RedAgentDataPipeItem {
158     ~RedVDIReadBuf() override;
159 
160     RedCharDeviceVDIPort *dev;
161 };
162 
163 enum VDIPortReadStates {
164     VDI_PORT_READ_STATE_READ_HEADER,
165     VDI_PORT_READ_STATE_GET_BUFF,
166     VDI_PORT_READ_STATE_READ_DATA,
167 };
168 
169 struct RedCharDeviceVDIPortPrivate {
170     bool agent_attached;
171     uint32_t plug_generation;
172     bool client_agent_started;
173     bool agent_supports_graphics_device_info;
174 
175     /* write to agent */
176     RedCharDeviceWriteBuffer *recv_from_client_buf;
177     int recv_from_client_buf_pushed;
178     AgentMsgFilter write_filter;
179 
180     /* read from agent */
181     uint32_t num_read_buf;
182     VDIPortReadStates read_state;
183     uint32_t message_receive_len;
184     uint8_t *receive_pos;
185     uint32_t receive_len;
186     red::shared_ptr<RedVDIReadBuf> current_read_buf;
187     AgentMsgFilter read_filter;
188 
189     VDIChunkHeader vdi_chunk_header;
190 
191     SpiceMigrateDataMain *mig_data; /* storing it when migration data arrives
192                                        before agent is attached */
193 };
194 
195 /* messages that are addressed to the agent and are created in the server */
196 #include <spice/start-packed.h>
197 struct SPICE_ATTR_PACKED VDInternalBuf {
198     VDIChunkHeader chunk_header;
199     VDAgentMessage header;
200     union {
201         VDAgentMouseState mouse_state;
202         VDAgentGraphicsDeviceInfo graphics_device_info;
203     }
204     u;
205 };
206 #include <spice/end-packed.h>
207 
208 struct RedCharDeviceVDIPort: public RedCharDevice
209 {
210     explicit RedCharDeviceVDIPort(RedsState *reds);
211     RedCharDeviceVDIPort();
212     ~RedCharDeviceVDIPort() override;
213 
214     RedPipeItemPtr read_one_msg_from_device() override;
215     void send_msg_to_client(RedPipeItem *msg, RedCharDeviceClientOpaque *opaque) override;
216     void send_tokens_to_client(RedCharDeviceClientOpaque *opaque, uint32_t tokens) override;
217     void remove_client(RedCharDeviceClientOpaque *opaque) override;
218     void on_free_self_token() override;
219 
220     RedCharDeviceVDIPortPrivate priv[1];
221 };
222 
223 static void migrate_timeout(RedsState *reds);
224 static RedsMigTargetClient* reds_mig_target_client_find(RedsState *reds, RedClient *client);
225 static void reds_mig_target_client_free(RedsState *reds, RedsMigTargetClient *mig_client);
226 static void reds_mig_cleanup_wait_disconnect(RedsState *reds);
227 static void reds_mig_remove_wait_disconnect_client(RedsState *reds, RedClient *client);
228 static void reds_add_char_device(RedsState *reds, const red::shared_ptr<RedCharDevice> &dev);
229 static void reds_send_mm_time(RedsState *reds);
230 static void reds_on_ic_change(RedsState *reds);
231 static void reds_on_sv_change(RedsState *reds);
232 static void reds_on_vc_change(RedsState *reds);
233 static void reds_on_vm_stop(RedsState *reds);
234 static void reds_on_vm_start(RedsState *reds);
235 static void reds_set_mouse_mode(RedsState *reds, SpiceMouseMode mode);
236 static uint32_t reds_qxl_ram_size(RedsState *reds);
237 static int calc_compression_level(RedsState *reds);
238 
239 static red::shared_ptr<RedVDIReadBuf> vdi_port_get_read_buf(RedCharDeviceVDIPort *dev);
240 
reds_find_channel_security(RedsState * reds,int id)241 static ChannelSecurityOptions *reds_find_channel_security(RedsState *reds, int id)
242 {
243     ChannelSecurityOptions *now = reds->config->channels_security;
244     while (now && now->channel_id != id) {
245         now = now->next;
246     }
247     return now;
248 }
249 
reds_handle_channel_event(RedsState * reds,int event,SpiceChannelEventInfo * info)250 void reds_handle_channel_event(RedsState *reds, int event, SpiceChannelEventInfo *info)
251 {
252     reds->core.channel_event(&reds->core, event, info);
253 
254     if (event == SPICE_CHANNEL_EVENT_DISCONNECTED) {
255         g_free(info);
256     }
257 }
258 
reds_link_free(RedLinkInfo * link)259 static void reds_link_free(RedLinkInfo *link)
260 {
261     red_stream_free(link->stream);
262     link->stream = nullptr;
263 
264     g_free(link->link_mess);
265     link->link_mess = nullptr;
266 
267     BN_free(link->tiTicketing.bn);
268     link->tiTicketing.bn = nullptr;
269 
270     if (link->tiTicketing.rsa) {
271         RSA_free(link->tiTicketing.rsa);
272         link->tiTicketing.rsa = nullptr;
273     }
274 
275     g_free(link);
276 }
277 
278 #ifdef RED_STATISTICS
279 
stat_init_node(RedStatNode * node,SpiceServer * reds,const RedStatNode * parent,const char * name,int visible)280 void stat_init_node(RedStatNode *node, SpiceServer *reds, const RedStatNode *parent,
281                     const char *name, int visible)
282 {
283     StatNodeRef parent_ref = parent ? parent->ref : INVALID_STAT_REF;
284     node->ref = stat_file_add_node(reds->stat_file, parent_ref, name, visible);
285 }
286 
stat_remove_node(SpiceServer * reds,RedStatNode * node)287 void stat_remove_node(SpiceServer *reds, RedStatNode *node)
288 {
289     if (node->ref != INVALID_STAT_REF) {
290         stat_file_remove_node(reds->stat_file, node->ref);
291         node->ref = INVALID_STAT_REF;
292     }
293 }
294 
stat_init_counter(RedStatCounter * counter,SpiceServer * reds,const RedStatNode * parent,const char * name,int visible)295 void stat_init_counter(RedStatCounter *counter, SpiceServer *reds,
296                        const RedStatNode *parent, const char *name, int visible)
297 {
298     StatNodeRef parent_ref = parent ? parent->ref : INVALID_STAT_REF;
299     counter->counter =
300         stat_file_add_counter(reds->stat_file, parent_ref, name, visible);
301 }
302 
stat_remove_counter(SpiceServer * reds,RedStatCounter * counter)303 void stat_remove_counter(SpiceServer *reds, RedStatCounter *counter)
304 {
305     if (counter->counter) {
306         stat_file_remove_counter(reds->stat_file, counter->counter);
307         counter->counter = NULL;
308     }
309 }
310 
311 #endif
312 
reds_register_channel(RedsState * reds,RedChannel * channel)313 void reds_register_channel(RedsState *reds, RedChannel *channel)
314 {
315     spice_assert(reds);
316 
317     uint32_t this_type = channel->type();
318     uint32_t this_id = channel->id();
319     if (spice_extra_checks) {
320         g_assert(reds_find_channel(reds, this_type, this_id) == nullptr);
321     } else {
322         g_warn_if_fail(reds_find_channel(reds, this_type, this_id) == nullptr);
323     }
324     reds->channels.push_front(red::shared_ptr<RedChannel>(channel));
325     // create new channel in the client if possible
326     reds->main_channel->registered_new_channel(channel);
327 }
328 
reds_unregister_channel(RedsState * reds,RedChannel * channel)329 void reds_unregister_channel(RedsState *reds, RedChannel *channel)
330 {
331     reds->channels.remove(red::shared_ptr<RedChannel>(channel));
332 }
333 
reds_find_channel(RedsState * reds,uint32_t type,uint32_t id)334 RedChannel *reds_find_channel(RedsState *reds, uint32_t type, uint32_t id)
335 {
336     for (const auto& channel: reds->channels) {
337         if (channel->type() == type && channel->id() == id) {
338             return channel.get();
339         }
340     }
341     return nullptr;
342 }
343 
344 /* Search for first free channel id for a specific channel type.
345  * Return first id free or <0 if not found. */
reds_get_free_channel_id(RedsState * reds,uint32_t type)346 int reds_get_free_channel_id(RedsState *reds, uint32_t type)
347 {
348     // this mark if some IDs are used.
349     // The size of the array limits the possible id returned but
350     // usually the IDs used for a channel type are not much.
351     bool used_ids[256];
352 
353     unsigned n;
354 
355     // mark id used for the specific channel type
356     memset(used_ids, 0, sizeof(used_ids));
357     for (const auto &channel: reds->channels) {
358         if (channel->type() == type && channel->id() < SPICE_N_ELEMENTS(used_ids)) {
359             used_ids[channel->id()] = true;
360         }
361     }
362 
363     // find first ID not marked as used
364     for (n = 0; n < SPICE_N_ELEMENTS(used_ids); ++n) {
365         if (!used_ids[n]) {
366             return n;
367         }
368     }
369     return -1;
370 }
371 
reds_mig_cleanup(RedsState * reds)372 static void reds_mig_cleanup(RedsState *reds)
373 {
374     if (reds->mig_inprogress) {
375 
376         if (reds->mig_wait_connect || reds->mig_wait_disconnect) {
377             SpiceMigrateInterface *sif;
378             spice_assert(reds->migration_interface);
379             sif = SPICE_UPCAST(SpiceMigrateInterface, reds->migration_interface->base.sif);
380             if (reds->mig_wait_connect) {
381                 sif->migrate_connect_complete(reds->migration_interface);
382             } else {
383                 if (sif->migrate_end_complete) {
384                     sif->migrate_end_complete(reds->migration_interface);
385                 }
386             }
387         }
388         reds->mig_inprogress = FALSE;
389         reds->mig_wait_connect = FALSE;
390         reds->mig_wait_disconnect = FALSE;
391         red_timer_cancel(reds->mig_timer);
392         reds_mig_cleanup_wait_disconnect(reds);
393     }
394 }
395 
reds_reset_vdp(RedsState * reds)396 static void reds_reset_vdp(RedsState *reds)
397 {
398     RedCharDeviceVDIPort *dev = reds->agent_dev.get();
399     SpiceCharDeviceInterface *sif;
400 
401     dev->priv->read_state = VDI_PORT_READ_STATE_READ_HEADER;
402     dev->priv->receive_pos = (uint8_t *)&dev->priv->vdi_chunk_header;
403     dev->priv->receive_len = sizeof(dev->priv->vdi_chunk_header);
404     dev->priv->message_receive_len = 0;
405     dev->priv->current_read_buf.reset();
406 
407     /* Reset read filter to start with clean state when the agent reconnects */
408     agent_msg_filter_init(&dev->priv->read_filter, reds->config->agent_copypaste,
409                           reds->config->agent_file_xfer,
410                           reds_use_client_monitors_config(reds), TRUE);
411     /* Throw away pending chunks from the current (if any) and future
412      * messages written by the client.
413      * TODO: client should clear its agent messages queue when the agent
414      * is disconnected. Currently, when an agent gets disconnected and reconnected,
415      * messages that were directed to the previous instance of the agent continue
416      * to be sent from the client. This TODO will require server, protocol, and client changes */
417     dev->priv->write_filter.result = AGENT_MSG_FILTER_DISCARD;
418     dev->priv->write_filter.discard_all = TRUE;
419     dev->priv->client_agent_started = false;
420     dev->priv->agent_supports_graphics_device_info = false;
421 
422     /*  The client's tokens are set once when the main channel is initialized
423      *  and once upon agent's connection with SPICE_MSG_MAIN_AGENT_CONNECTED_TOKENS.
424      *  The client tokens are tracked as part of the RedCharDeviceClient. Thus,
425      *  in order to be backward compatible with the client, we need to track the tokens
426      *  even if the agent is detached. We don't destroy the char_device, and
427      *  instead we just reset it.
428      *  The tokens are also reset to avoid mismatch in upon agent reconnection.
429      */
430     dev->priv->agent_attached = FALSE;
431     dev->stop();
432     dev->reset();
433     dev->reset_dev_instance(nullptr);
434 
435     sif = spice_char_device_get_interface(reds->vdagent);
436     if (sif->state) {
437         sif->state(reds->vdagent, 0);
438     }
439 }
440 
vdagent_new_write_buffer(RedCharDeviceVDIPort * agent_dev,uint32_t type,size_t size,bool use_token)441 static RedCharDeviceWriteBuffer *vdagent_new_write_buffer(RedCharDeviceVDIPort *agent_dev,
442                                                           uint32_t type,
443                                                           size_t size,
444                                                           bool use_token)
445 {
446     uint32_t total_msg_size = sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) + size;
447 
448     RedCharDeviceWriteBuffer *char_dev_buf;
449         char_dev_buf = agent_dev->write_buffer_get_server(total_msg_size,
450                                                           use_token);
451     if (!char_dev_buf) {
452         return nullptr;  // no token was available
453     }
454 
455     char_dev_buf->buf_used = total_msg_size;
456     auto internal_buf = (VDInternalBuf *)char_dev_buf->buf;
457     internal_buf->chunk_header.port = VDP_SERVER_PORT;
458     internal_buf->chunk_header.size = sizeof(VDAgentMessage) + size;
459     internal_buf->header.protocol = VD_AGENT_PROTOCOL;
460     internal_buf->header.type = type;
461     internal_buf->header.opaque = 0;
462     internal_buf->header.size = size;
463 
464     return char_dev_buf;
465 }
466 
reds_main_channel_connected(RedsState * reds)467 static int reds_main_channel_connected(RedsState *reds)
468 {
469     return reds->main_channel && reds->main_channel->is_connected();
470 }
471 
reds_client_disconnect(RedsState * reds,RedClient * client)472 void reds_client_disconnect(RedsState *reds, RedClient *client)
473 {
474     RedsMigTargetClient *mig_client;
475 
476     if (reds->config->exit_on_disconnect)
477     {
478         spice_debug("Exiting server because of client disconnect.");
479         exit(0);
480     }
481 
482     if (!client || client->is_disconnecting()) {
483         spice_debug("client %p already during disconnection", client);
484         return;
485     }
486 
487     spice_debug("trace");
488     /* disconnecting is set to prevent recursion because of the following:
489      * main_channel_client_on_disconnect->
490      *  reds_client_disconnect->red_client_destroy->main_channel...
491      */
492     client->set_disconnecting();
493 
494     // TODO: we need to handle agent properly for all clients!!!! (e.g., cut and paste, how?)
495     // We shouldn't initialize the agent when there are still clients connected
496 
497     mig_client = reds_mig_target_client_find(reds, client);
498     if (mig_client) {
499         reds_mig_target_client_free(reds, mig_client);
500     }
501 
502     if (reds->mig_wait_disconnect) {
503         reds_mig_remove_wait_disconnect_client(reds, client);
504     }
505 
506     /* note that client might be NULL, if the vdagent was once
507      * up and than was removed */
508     auto client_opaque = (RedCharDeviceClientOpaque *) client;
509     if (reds->agent_dev->client_exists(client_opaque)) {
510         reds->agent_dev->client_remove(client_opaque);
511     }
512 
513     reds->clients.remove(client);
514     client->destroy();
515 
516    // TODO: we need to handle agent properly for all clients!!!! (e.g., cut and paste, how? Maybe throw away messages
517    // if we are in the middle of one from another client)
518     if (reds->clients.empty()) {
519         /* Let the agent know the client is disconnected */
520         if (reds->agent_dev->priv->agent_attached) {
521             RedCharDeviceWriteBuffer *char_dev_buf =
522                 vdagent_new_write_buffer(reds->agent_dev.get(),
523                                          VD_AGENT_CLIENT_DISCONNECTED,
524                                          0,
525                                          false);
526 
527             reds->agent_dev->write_buffer_add(char_dev_buf);
528         }
529 
530         /* Reset write filter to start with clean state on client reconnect */
531         agent_msg_filter_init(&reds->agent_dev->priv->write_filter, reds->config->agent_copypaste,
532                               reds->config->agent_file_xfer,
533                               reds_use_client_monitors_config(reds), TRUE);
534 
535         /* Throw away pending chunks from the current (if any) and future
536          *  messages read from the agent */
537         reds->agent_dev->priv->read_filter.result = AGENT_MSG_FILTER_DISCARD;
538         reds->agent_dev->priv->read_filter.discard_all = TRUE;
539         g_free(reds->agent_dev->priv->mig_data);
540         reds->agent_dev->priv->mig_data = nullptr;
541 
542         reds_mig_cleanup(reds);
543     }
544 }
545 
546 // TODO: go over all usage of reds_disconnect, most/some of it should be converted to
547 // reds_client_disconnect
reds_disconnect(RedsState * reds)548 static void reds_disconnect(RedsState *reds)
549 {
550     spice_debug("trace");
551     for (auto client: reds->clients) {
552         reds_client_disconnect(reds, client);
553     }
554     reds_mig_cleanup(reds);
555 }
556 
reds_mig_disconnect(RedsState * reds)557 static void reds_mig_disconnect(RedsState *reds)
558 {
559     if (reds_main_channel_connected(reds)) {
560         reds_disconnect(reds);
561     } else {
562         reds_mig_cleanup(reds);
563     }
564 }
565 
reds_config_get_playback_compression(RedsState * reds)566 bool reds_config_get_playback_compression(RedsState *reds)
567 {
568     return reds->config->playback_compression;
569 }
570 
reds_get_mouse_mode(RedsState * reds)571 SpiceMouseMode reds_get_mouse_mode(RedsState *reds)
572 {
573     return reds->mouse_mode;
574 }
575 
reds_set_mouse_mode(RedsState * reds,SpiceMouseMode mode)576 static void reds_set_mouse_mode(RedsState *reds, SpiceMouseMode mode)
577 {
578     if (reds->mouse_mode == mode) {
579         return;
580     }
581     reds->mouse_mode = mode;
582 
583     FOREACH_QXL_INSTANCE(reds, qxl) {
584         red_qxl_set_mouse_mode(qxl, mode);
585     }
586 
587     reds->main_channel->push_mouse_mode(reds->mouse_mode,
588                                         reds->is_client_mouse_allowed);
589 }
590 
reds_config_get_agent_mouse(const RedsState * reds)591 gboolean reds_config_get_agent_mouse(const RedsState *reds)
592 {
593     return reds->config->agent_mouse;
594 }
595 
reds_update_mouse_mode(RedsState * reds)596 static void reds_update_mouse_mode(RedsState *reds)
597 {
598     int allowed = 0;
599     int qxl_count = reds->qxl_instances.size();
600     int display_channel_count = 0;
601 
602     for (const auto &channel: reds->channels) {
603         if (channel->type() == SPICE_CHANNEL_DISPLAY) {
604             ++display_channel_count;
605         }
606     }
607 
608     if ((reds->config->agent_mouse && reds->vdagent) ||
609         (reds->inputs_channel && reds->inputs_channel->has_tablet() &&
610             qxl_count == 1 && display_channel_count == 1)) {
611         allowed = reds->dispatcher_allows_client_mouse;
612     }
613     if (allowed == reds->is_client_mouse_allowed) {
614         return;
615     }
616     reds->is_client_mouse_allowed = allowed;
617     if (reds->mouse_mode == SPICE_MOUSE_MODE_CLIENT && !allowed) {
618         reds_set_mouse_mode(reds, SPICE_MOUSE_MODE_SERVER);
619         return;
620     }
621     if (reds->main_channel) {
622         reds->main_channel->push_mouse_mode(reds->mouse_mode,
623                                             reds->is_client_mouse_allowed);
624     }
625 }
626 
reds_update_agent_properties(RedsState * reds)627 static void reds_update_agent_properties(RedsState *reds)
628 {
629     if (!reds->agent_dev || reds->config == nullptr) {
630         return;
631     }
632     /* copy & paste */
633     reds->agent_dev->priv->write_filter.copy_paste_enabled = reds->config->agent_copypaste;
634     reds->agent_dev->priv->read_filter.copy_paste_enabled = reds->config->agent_copypaste;
635     /* file transfer */
636     reds->agent_dev->priv->write_filter.file_xfer_enabled = reds->config->agent_file_xfer;
637     reds->agent_dev->priv->read_filter.file_xfer_enabled = reds->config->agent_file_xfer;
638 }
639 
reds_agent_remove(RedsState * reds)640 static void reds_agent_remove(RedsState *reds)
641 {
642     // TODO: agent is broken with multiple clients. also need to figure out what to do when
643     // part of the clients are during target migration.
644     reds_reset_vdp(reds);
645 
646     reds->vdagent = nullptr;
647     reds_update_mouse_mode(reds);
648     if (reds_main_channel_connected(reds) &&
649         !reds->main_channel->is_waiting_for_migrate_data()) {
650         reds->main_channel->push_agent_disconnected();
651     }
652 }
653 
654 /*
655     returns the #AgentMsgFilterResult value:
656         AGENT_MSG_FILTER_OK if the buffer can be forwarded,
657         AGENT_MSG_FILTER_PROTO_ERROR on error
658         other values can be discarded
659 */
vdi_port_read_buf_process(RedCharDeviceVDIPort * dev,RedVDIReadBuf & buf)660 static AgentMsgFilterResult vdi_port_read_buf_process(RedCharDeviceVDIPort *dev,
661                                                       RedVDIReadBuf& buf)
662 {
663     switch (dev->priv->vdi_chunk_header.port) {
664     case VDP_CLIENT_PORT:
665         return agent_msg_filter_process_data(&dev->priv->read_filter, buf.data, buf.len);
666     case VDP_SERVER_PORT:
667         return AGENT_MSG_FILTER_DISCARD;
668     default:
669         spice_warning("invalid port");
670         return AGENT_MSG_FILTER_PROTO_ERROR;
671     }
672 }
673 
~RedVDIReadBuf()674 RedVDIReadBuf::~RedVDIReadBuf()
675 {
676     dev->priv->num_read_buf--;
677 
678     /* read_one_msg_from_vdi_port may have never completed because we
679        reached buffer limit. So we call it again so it can complete its work if
680        necessary. Note that since we can be called from red_char_device_wakeup
681        this can cause recursion, but we have protection for that */
682     if (dev->priv->agent_attached) {
683        dev->wakeup();
684     }
685 }
686 
vdi_read_buf_new(RedCharDeviceVDIPort * dev)687 static red::shared_ptr<RedVDIReadBuf> vdi_read_buf_new(RedCharDeviceVDIPort *dev)
688 {
689     auto buf = red::make_shared<RedVDIReadBuf>();
690     buf->dev = dev;
691     return buf;
692 }
693 
vdi_port_get_read_buf(RedCharDeviceVDIPort * dev)694 static red::shared_ptr<RedVDIReadBuf> vdi_port_get_read_buf(RedCharDeviceVDIPort *dev)
695 {
696     if (dev->priv->num_read_buf >= REDS_VDI_PORT_NUM_RECEIVE_BUFFS) {
697         return red::shared_ptr<RedVDIReadBuf>();
698     }
699 
700     dev->priv->num_read_buf++;
701     return vdi_read_buf_new(dev);
702 }
703 
704 /* certain agent capabilities can be overridden and disabled in the server. In these cases, unset
705  * these capabilities before sending them on to the client */
reds_adjust_agent_capabilities(RedsState * reds,VDAgentMessage * message)706 static void reds_adjust_agent_capabilities(RedsState *reds, VDAgentMessage *message)
707 {
708     VDAgentAnnounceCapabilities *capabilities;
709 
710     if (message->type != VD_AGENT_ANNOUNCE_CAPABILITIES) {
711         return;
712     }
713     capabilities = (VDAgentAnnounceCapabilities *) message->data;
714 
715     if (!reds->config->agent_copypaste) {
716         VD_AGENT_CLEAR_CAPABILITY(capabilities->caps, VD_AGENT_CAP_CLIPBOARD);
717         VD_AGENT_CLEAR_CAPABILITY(capabilities->caps, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND);
718         VD_AGENT_CLEAR_CAPABILITY(capabilities->caps, VD_AGENT_CAP_CLIPBOARD_SELECTION);
719     }
720 
721     if (!reds->config->agent_file_xfer) {
722         VD_AGENT_SET_CAPABILITY(capabilities->caps, VD_AGENT_CAP_FILE_XFER_DISABLED);
723     }
724 
725     size_t caps_size = VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(message->size);
726     reds->agent_dev->priv->agent_supports_graphics_device_info =
727         VD_AGENT_HAS_CAPABILITY(capabilities->caps, caps_size, VD_AGENT_CAP_GRAPHICS_DEVICE_INFO);
728     reds_send_device_display_info(reds);
729 }
730 
731 /* reads from the device till completes reading a message that is addressed to the client,
732  * or otherwise, when reading from the device fails */
733 RedPipeItemPtr
read_one_msg_from_device()734 RedCharDeviceVDIPort::read_one_msg_from_device()
735 {
736     RedsState *reds;
737     int n;
738 
739     reds = get_server();
740     while (reds->vdagent) {
741         switch (priv->read_state) {
742         case VDI_PORT_READ_STATE_READ_HEADER:
743             n = read(priv->receive_pos, priv->receive_len);
744             if (!n) {
745                 return RedPipeItemPtr();
746             }
747             if ((priv->receive_len -= n)) {
748                 priv->receive_pos += n;
749                 return RedPipeItemPtr();
750             }
751             priv->message_receive_len = priv->vdi_chunk_header.size;
752             priv->read_state = VDI_PORT_READ_STATE_GET_BUFF;
753             /* fall through */
754         case VDI_PORT_READ_STATE_GET_BUFF: {
755             if (!(priv->current_read_buf = vdi_port_get_read_buf(this))) {
756                 return RedPipeItemPtr();
757             }
758             priv->receive_pos = priv->current_read_buf->data;
759             priv->receive_len = MIN(priv->message_receive_len,
760                                     sizeof(priv->current_read_buf->data));
761             priv->current_read_buf->len = priv->receive_len;
762             priv->message_receive_len -= priv->receive_len;
763             priv->read_state = VDI_PORT_READ_STATE_READ_DATA;
764         }
765             /* fall through */
766         case VDI_PORT_READ_STATE_READ_DATA: {
767             n = read(priv->receive_pos, priv->receive_len);
768             if (!n) {
769                 return RedPipeItemPtr();
770             }
771             if ((priv->receive_len -= n)) {
772                 priv->receive_pos += n;
773                 break;
774             }
775             auto dispatch_buf = std::move(priv->current_read_buf);
776             priv->receive_pos = nullptr;
777             if (priv->message_receive_len == 0) {
778                 priv->read_state = VDI_PORT_READ_STATE_READ_HEADER;
779                 priv->receive_pos = (uint8_t *)&priv->vdi_chunk_header;
780                 priv->receive_len = sizeof(priv->vdi_chunk_header);
781             } else {
782                 priv->read_state = VDI_PORT_READ_STATE_GET_BUFF;
783             }
784             switch (vdi_port_read_buf_process(this, *dispatch_buf)) {
785             case AGENT_MSG_FILTER_OK:
786                 reds_adjust_agent_capabilities(reds, (VDAgentMessage *) dispatch_buf->data);
787                 return dispatch_buf;
788             case AGENT_MSG_FILTER_PROTO_ERROR:
789                 reds_agent_remove(reds);
790                 /* fall through */
791             case AGENT_MSG_FILTER_MONITORS_CONFIG:
792                 /* fall through */
793             case AGENT_MSG_FILTER_DISCARD:
794                 dispatch_buf.reset();
795             }
796         }
797         } /* END switch */
798     } /* END while */
799     return RedPipeItemPtr();
800 }
801 
reds_marshall_device_display_info(RedsState * reds,SpiceMarshaller * m)802 void reds_marshall_device_display_info(RedsState *reds, SpiceMarshaller *m)
803 {
804     uint32_t device_count = 0;
805     void *device_count_ptr = spice_marshaller_add_uint32(m, device_count);
806 
807     // add the qxl devices to the message
808     FOREACH_QXL_INSTANCE(reds, qxl) {
809         device_count += red_qxl_marshall_device_display_info(qxl, m);
810     }
811 
812     // add the stream devices to the message
813     for (const auto& dev: reds->char_devices) {
814         auto stream_dev = dynamic_cast<StreamDevice*>(dev.get());
815         if (stream_dev) {
816             const StreamDeviceDisplayInfo *info = stream_dev->get_device_display_info();
817             size_t device_address_len = strlen(info->device_address) + 1;
818 
819             if (device_address_len == 1) {
820                 // the device info wasn't set (yet), don't send it
821                 continue;
822             }
823 
824             int32_t channel_id = stream_dev->get_stream_channel_id();
825             if (channel_id == -1) {
826                 g_warning("DeviceDisplayInfo set but no stream channel exists");
827                 continue;
828             }
829 
830             spice_marshaller_add_uint32(m, channel_id);
831             spice_marshaller_add_uint32(m, info->stream_id);
832             spice_marshaller_add_uint32(m, info->device_display_id);
833             spice_marshaller_add_uint32(m, device_address_len);
834             spice_marshaller_add(m, (const uint8_t*) (void*) info->device_address, device_address_len);
835             ++device_count;
836 
837             g_debug("   (stream) channel_id: %u monitor_id: %u, device_address: %s, "
838                     "device_display_id: %u",
839                     channel_id, info->stream_id, info->device_address,
840                     info->device_display_id);
841         }
842     }
843     spice_marshaller_set_uint32(m, device_count_ptr, device_count);
844 }
845 
reds_send_device_display_info(RedsState * reds)846 void reds_send_device_display_info(RedsState *reds)
847 {
848     if (!reds->agent_dev->priv->agent_attached) {
849         return;
850     }
851     if (!reds->agent_dev->priv->agent_supports_graphics_device_info) {
852         return;
853     }
854 
855     g_debug("Sending device display info to the agent:");
856 
857     SpiceMarshaller *m = spice_marshaller_new();
858     reds_marshall_device_display_info(reds, m);
859 
860     RedCharDeviceWriteBuffer *char_dev_buf = vdagent_new_write_buffer(reds->agent_dev.get(),
861                                          VD_AGENT_GRAPHICS_DEVICE_INFO,
862                                          spice_marshaller_get_total_size(m),
863                                          true);
864 
865     if (!char_dev_buf) {
866         spice_marshaller_destroy(m);
867         reds->pending_device_display_info_message = true;
868         return;
869     }
870 
871     auto internal_buf = (VDInternalBuf *)char_dev_buf->buf;
872 
873     int free_info;
874     size_t len_info;
875     uint8_t *info = spice_marshaller_linearize(m, 0, &len_info, &free_info);
876     memcpy(&internal_buf->u.graphics_device_info, info, len_info);
877     if (free_info) {
878         free(info);
879     }
880     spice_marshaller_destroy(m);
881 
882     reds->pending_device_display_info_message = false;
883 
884     reds->agent_dev->write_buffer_add(char_dev_buf);
885 }
886 
887 /* after calling this, we unref the message, and the ref is in the instance side */
send_msg_to_client(RedPipeItem * msg,RedCharDeviceClientOpaque * opaque)888 void RedCharDeviceVDIPort::send_msg_to_client(RedPipeItem *msg, RedCharDeviceClientOpaque *opaque)
889 {
890     auto client = (RedClient *) opaque;
891     auto agent_data_buf = static_cast<RedVDIReadBuf*>(msg);
892 
893     client->get_main()->push_agent_data(red::shared_ptr<RedAgentDataPipeItem>(agent_data_buf));
894 }
895 
send_tokens_to_client(RedCharDeviceClientOpaque * opaque,uint32_t tokens)896 void RedCharDeviceVDIPort::send_tokens_to_client(RedCharDeviceClientOpaque *opaque, uint32_t tokens)
897 {
898     auto client = (RedClient *) opaque;
899     client->get_main()->push_agent_tokens(tokens);
900 }
901 
on_free_self_token()902 void RedCharDeviceVDIPort::on_free_self_token()
903 {
904     RedsState *reds = get_server();
905 
906     if (reds->inputs_channel && reds->pending_mouse_event) {
907         spice_debug("pending mouse event");
908         reds_handle_agent_mouse_event(reds, reds->inputs_channel->get_mouse_state());
909     }
910 
911     if (reds->pending_device_display_info_message) {
912         spice_debug("pending device display info message");
913         reds_send_device_display_info(reds);
914     }
915 }
916 
remove_client(RedCharDeviceClientOpaque * opaque)917 void RedCharDeviceVDIPort::remove_client(RedCharDeviceClientOpaque *opaque)
918 {
919     auto client = (RedClient *) opaque;
920     client->get_main()->shutdown();
921 }
922 
923 /****************************************************************************/
924 
reds_has_vdagent(RedsState * reds)925 int reds_has_vdagent(RedsState *reds)
926 {
927     return !!reds->vdagent;
928 }
929 
reds_handle_agent_mouse_event(RedsState * reds,const VDAgentMouseState * mouse_state)930 void reds_handle_agent_mouse_event(RedsState *reds, const VDAgentMouseState *mouse_state)
931 {
932     if (!reds->inputs_channel || !reds->agent_dev->priv->agent_attached) {
933         return;
934     }
935 
936     RedCharDeviceWriteBuffer *char_dev_buf = vdagent_new_write_buffer(reds->agent_dev.get(),
937                                                                       VD_AGENT_MOUSE_STATE,
938                                                                       sizeof(VDAgentMouseState),
939                                                                       true);
940 
941     if (!char_dev_buf) {
942         reds->pending_mouse_event = TRUE;
943         return;
944     }
945 
946     reds->pending_mouse_event = FALSE;
947 
948     auto internal_buf = (VDInternalBuf *)char_dev_buf->buf;
949     internal_buf->u.mouse_state = *mouse_state;
950 
951     reds->agent_dev->write_buffer_add(char_dev_buf);
952 }
953 
spice_server_get_num_clients(SpiceServer * reds)954 SPICE_GNUC_VISIBLE int spice_server_get_num_clients(SpiceServer *reds)
955 {
956     return reds ? reds->clients.size() : 0;
957 }
958 
channel_supports_multiple_clients(const RedChannel * channel)959 static bool channel_supports_multiple_clients(const RedChannel *channel)
960 {
961     switch (channel->type()) {
962     case SPICE_CHANNEL_MAIN:
963     case SPICE_CHANNEL_DISPLAY:
964     case SPICE_CHANNEL_CURSOR:
965     case SPICE_CHANNEL_INPUTS:
966         return TRUE;
967     }
968     return FALSE;
969 }
970 
reds_fill_channels(RedsState * reds,SpiceMsgChannels * channels_info)971 static void reds_fill_channels(RedsState *reds, SpiceMsgChannels *channels_info)
972 {
973     int used_channels = 0;
974 
975     for (const auto &channel: reds->channels) {
976         if (reds->clients.size() > 1 &&
977             !channel_supports_multiple_clients(channel.get())) {
978             continue;
979         }
980         channels_info->channels[used_channels].type = channel->type();
981         channels_info->channels[used_channels].id = channel->id();
982         used_channels++;
983     }
984 
985     channels_info->num_of_channels = used_channels;
986     if (used_channels != reds->channels.size()) {
987         spice_warning("sent %d out of %zd", used_channels, reds->channels.size());
988     }
989 }
990 
reds_msg_channels_new(RedsState * reds)991 SpiceMsgChannels *reds_msg_channels_new(RedsState *reds)
992 {
993     SpiceMsgChannels* channels_info;
994 
995     spice_assert(reds != nullptr);
996 
997     channels_info = (SpiceMsgChannels *)g_malloc(sizeof(SpiceMsgChannels)
998                             + reds->channels.size() * sizeof(SpiceChannelId));
999 
1000     reds_fill_channels(reds, channels_info);
1001 
1002     return channels_info;
1003 }
1004 
reds_on_main_agent_start(RedsState * reds,MainChannelClient * mcc,uint32_t num_tokens)1005 void reds_on_main_agent_start(RedsState *reds, MainChannelClient *mcc, uint32_t num_tokens)
1006 {
1007     RedCharDevice *dev_state = reds->agent_dev.get();
1008     RedClient *client;
1009 
1010     if (!reds->vdagent) {
1011         return;
1012     }
1013     spice_assert(reds->vdagent->st && reds->vdagent->st == dev_state);
1014     client = mcc->get_client();
1015     reds->agent_dev->priv->client_agent_started = true;
1016     /*
1017      * Note that in older releases, send_tokens were set to ~0 on both client
1018      * and server. The server ignored the client given tokens.
1019      * Thanks to that, when an old client is connected to a new server,
1020      * and vice versa, the sending from the server to the client won't have
1021      * flow control, but will have no other problem.
1022      */
1023     auto client_opaque = (RedCharDeviceClientOpaque *) client;
1024     if (!dev_state->client_exists(client_opaque)) {
1025         int client_added;
1026 
1027         client_added = dev_state->client_add(client_opaque, TRUE,
1028                                              REDS_VDI_PORT_NUM_RECEIVE_BUFFS,
1029                                              REDS_AGENT_WINDOW_SIZE,
1030                                              num_tokens,
1031                                              mcc->is_waiting_for_migrate_data());
1032 
1033         if (!client_added) {
1034             spice_warning("failed to add client to agent");
1035             mcc->shutdown();
1036             return;
1037         }
1038     } else {
1039         dev_state->send_to_client_tokens_set(client_opaque, num_tokens);
1040     }
1041 
1042     reds_send_device_display_info(reds);
1043 
1044     agent_msg_filter_config(&reds->agent_dev->priv->write_filter, reds->config->agent_copypaste,
1045                             reds->config->agent_file_xfer,
1046                             reds_use_client_monitors_config(reds));
1047     reds->agent_dev->priv->write_filter.discard_all = FALSE;
1048 }
1049 
reds_on_main_agent_tokens(RedsState * reds,MainChannelClient * mcc,uint32_t num_tokens)1050 void reds_on_main_agent_tokens(RedsState *reds, MainChannelClient *mcc, uint32_t num_tokens)
1051 {
1052     RedClient *client = mcc->get_client();
1053     if (!reds->vdagent) {
1054         return;
1055     }
1056     spice_assert(reds->vdagent->st);
1057     reds->vdagent->st->send_to_client_tokens_add((RedCharDeviceClientOpaque *)client,
1058                                                  num_tokens);
1059 }
1060 
reds_get_agent_data_buffer(RedsState * reds,MainChannelClient * mcc,size_t size)1061 uint8_t *reds_get_agent_data_buffer(RedsState *reds, MainChannelClient *mcc, size_t size)
1062 {
1063     RedCharDeviceVDIPort *dev = reds->agent_dev.get();
1064     RedClient *client;
1065 
1066     if (!dev->priv->client_agent_started) {
1067         /*
1068          * agent got disconnected, and possibly got reconnected, but we still can receive
1069          * msgs that are addressed to the agent's old instance, in case they were
1070          * sent by the client before it received the AGENT_DISCONNECTED msg.
1071          * In such case, we will receive and discard the msgs (reds_reset_vdp takes care
1072          * of setting dev->write_filter.result = AGENT_MSG_FILTER_DISCARD).
1073          */
1074         return (uint8_t*) g_malloc(size);
1075     }
1076 
1077     spice_assert(dev->priv->recv_from_client_buf == nullptr);
1078     client = mcc->get_client();
1079     dev->priv->recv_from_client_buf =
1080         dev->write_buffer_get_client((RedCharDeviceClientOpaque *)client,
1081                                      size + sizeof(VDIChunkHeader));
1082     /* check if buffer was allocated, as flow control is enabled for
1083      * this device this is a normal condition */
1084     if (!dev->priv->recv_from_client_buf) {
1085         return nullptr;
1086     }
1087     dev->priv->recv_from_client_buf_pushed = FALSE;
1088     return dev->priv->recv_from_client_buf->buf + sizeof(VDIChunkHeader);
1089 }
1090 
reds_release_agent_data_buffer(RedsState * reds,uint8_t * buf)1091 void reds_release_agent_data_buffer(RedsState *reds, uint8_t *buf)
1092 {
1093     RedCharDeviceVDIPort *dev = reds->agent_dev.get();
1094 
1095     if (!dev->priv->recv_from_client_buf) {
1096         g_free(buf);
1097         return;
1098     }
1099 
1100     spice_assert(buf == dev->priv->recv_from_client_buf->buf + sizeof(VDIChunkHeader));
1101     /* if we pushed the buffer the buffer is attached to the channel so don't free it */
1102     if (!dev->priv->recv_from_client_buf_pushed) {
1103         RedCharDevice::write_buffer_release(dev,
1104                                             &dev->priv->recv_from_client_buf);
1105     }
1106     dev->priv->recv_from_client_buf = nullptr;
1107     dev->priv->recv_from_client_buf_pushed = FALSE;
1108 }
1109 
reds_on_main_agent_monitors_config(RedsState * reds,MainChannelClient * mcc,const void * message,size_t size)1110 static void reds_on_main_agent_monitors_config(RedsState *reds,
1111         MainChannelClient *mcc, const void *message, size_t size)
1112 {
1113     const unsigned int MAX_NUM_MONITORS = 256;
1114     const unsigned int MAX_MONITOR_CONFIG_SIZE =
1115         sizeof(VDAgentMonitorsConfig) +
1116         MAX_NUM_MONITORS * (sizeof(VDAgentMonConfig) + sizeof(VDAgentMonitorMM));
1117 
1118     VDAgentMessage *msg_header;
1119     VDAgentMonitorsConfig *monitors_config;
1120     SpiceBuffer *cmc = &reds->client_monitors_config;
1121     uint32_t msg_size;
1122 
1123     // limit size of message sent by the client as this can cause a DoS through
1124     // memory exhaustion, or potentially some integer overflows
1125     if (sizeof(VDAgentMessage) + MAX_MONITOR_CONFIG_SIZE - cmc->offset < size) {
1126         goto overflow;
1127     }
1128     spice_buffer_append(cmc, message, size);
1129     if (sizeof(VDAgentMessage) > cmc->offset) {
1130         spice_debug("not enough data yet. %" G_GSSIZE_FORMAT, cmc->offset);
1131         return;
1132     }
1133     msg_header = (VDAgentMessage *)cmc->buffer;
1134     msg_size = GUINT32_FROM_LE(msg_header->size);
1135     if (msg_size > MAX_MONITOR_CONFIG_SIZE) {
1136         goto overflow;
1137     }
1138     if (msg_size > cmc->offset - sizeof(VDAgentMessage)) {
1139         spice_debug("not enough data yet. %" G_GSSIZE_FORMAT, cmc->offset);
1140         return;
1141     }
1142 
1143     // convert VDAgentMessage endianness
1144     msg_header->protocol = GUINT32_FROM_LE(msg_header->protocol);
1145     msg_header->type = GUINT32_FROM_LE(msg_header->type);
1146     msg_header->opaque = GUINT64_FROM_LE(msg_header->opaque);
1147     msg_header->size = GUINT32_FROM_LE(msg_header->size);
1148 
1149     monitors_config = (VDAgentMonitorsConfig *)(cmc->buffer + sizeof(*msg_header));
1150     if (agent_check_message(msg_header, (uint8_t *) monitors_config,
1151                             nullptr, 0) != AGENT_CHECK_NO_ERROR) {
1152         goto overflow;
1153     }
1154     spice_debug("monitors_config->num_of_monitors: %d", monitors_config->num_of_monitors);
1155     reds_client_monitors_config(reds, monitors_config);
1156     spice_buffer_free(cmc);
1157     return;
1158 
1159 overflow:
1160     spice_warning("received invalid MonitorsConfig request from client, disconnecting");
1161     mcc->disconnect();
1162     spice_buffer_free(cmc);
1163 }
1164 
reds_on_main_agent_data(RedsState * reds,MainChannelClient * mcc,const void * message,size_t size)1165 void reds_on_main_agent_data(RedsState *reds, MainChannelClient *mcc, const void *message,
1166                              size_t size)
1167 {
1168     RedCharDeviceVDIPort *dev = reds->agent_dev.get();
1169     VDIChunkHeader *header;
1170     AgentMsgFilterResult res;
1171 
1172     res = agent_msg_filter_process_data(&dev->priv->write_filter,
1173                                         (const uint8_t*) message, size);
1174     switch (res) {
1175     case AGENT_MSG_FILTER_OK:
1176         break;
1177     case AGENT_MSG_FILTER_DISCARD:
1178         return;
1179     case AGENT_MSG_FILTER_MONITORS_CONFIG:
1180         reds_on_main_agent_monitors_config(reds, mcc, message, size);
1181         return;
1182     case AGENT_MSG_FILTER_PROTO_ERROR:
1183         mcc->shutdown();
1184         return;
1185     }
1186 
1187     spice_assert(dev->priv->recv_from_client_buf);
1188     spice_assert(message == dev->priv->recv_from_client_buf->buf + sizeof(VDIChunkHeader));
1189     // TODO - start tracking agent data per channel
1190     header =  (VDIChunkHeader *)dev->priv->recv_from_client_buf->buf;
1191     header->port = VDP_CLIENT_PORT;
1192     header->size = size;
1193     dev->priv->recv_from_client_buf->buf_used = sizeof(VDIChunkHeader) + size;
1194 
1195     dev->priv->recv_from_client_buf_pushed = TRUE;
1196     dev->write_buffer_add(dev->priv->recv_from_client_buf);
1197 }
1198 
reds_on_main_migrate_connected(RedsState * reds,int seamless)1199 void reds_on_main_migrate_connected(RedsState *reds, int seamless)
1200 {
1201     reds->src_do_seamless_migrate = seamless;
1202     if (reds->mig_wait_connect) {
1203         reds_mig_cleanup(reds);
1204     }
1205 }
1206 
reds_on_main_mouse_mode_request(RedsState * reds,void * message,size_t size)1207 void reds_on_main_mouse_mode_request(RedsState *reds, void *message, size_t size)
1208 {
1209     switch (((SpiceMsgcMainMouseModeRequest *)message)->mode) {
1210     case SPICE_MOUSE_MODE_CLIENT:
1211         if (reds->is_client_mouse_allowed) {
1212             reds_set_mouse_mode(reds, SPICE_MOUSE_MODE_CLIENT);
1213         } else {
1214             spice_debug("client mouse is disabled");
1215         }
1216         break;
1217     case SPICE_MOUSE_MODE_SERVER:
1218         reds_set_mouse_mode(reds, SPICE_MOUSE_MODE_SERVER);
1219         break;
1220     default:
1221         spice_warning("unsupported mouse mode");
1222     }
1223 }
1224 
1225 /*
1226  * Push partial agent data, even if not all the chunk was consumend,
1227  * in order to avoid the roundtrip (src-server->client->dest-server)
1228  */
reds_on_main_channel_migrate(RedsState * reds,MainChannelClient * mcc)1229 void reds_on_main_channel_migrate(RedsState *reds, MainChannelClient *mcc)
1230 {
1231     RedCharDeviceVDIPort *agent_dev = reds->agent_dev.get();
1232     uint32_t read_data_len;
1233 
1234     spice_assert(reds->clients.size() == 1);
1235 
1236     if (agent_dev->priv->read_state != VDI_PORT_READ_STATE_READ_DATA) {
1237         return;
1238     }
1239     spice_assert(agent_dev->priv->current_read_buf &&
1240                  agent_dev->priv->receive_pos > agent_dev->priv->current_read_buf->data);
1241     read_data_len = agent_dev->priv->receive_pos - agent_dev->priv->current_read_buf->data;
1242 
1243     if (agent_dev->priv->read_filter.msg_data_to_read ||
1244         read_data_len > sizeof(VDAgentMessage)) { /* msg header has been read */
1245         red::shared_ptr<RedVDIReadBuf> read_buf = std::move(agent_dev->priv->current_read_buf);
1246 
1247         spice_debug("push partial read %u (msg first chunk? %d)", read_data_len,
1248                     !agent_dev->priv->read_filter.msg_data_to_read);
1249 
1250         read_buf->len = read_data_len;
1251         switch (vdi_port_read_buf_process(agent_dev, *read_buf)) {
1252         case AGENT_MSG_FILTER_OK:
1253             reds_adjust_agent_capabilities(reds, (VDAgentMessage *)read_buf->data);
1254             mcc->push_agent_data(read_buf);
1255             break;
1256         case AGENT_MSG_FILTER_PROTO_ERROR:
1257             reds_agent_remove(reds);
1258             /* fall through */
1259         case AGENT_MSG_FILTER_MONITORS_CONFIG:
1260             /* fall through */
1261         case AGENT_MSG_FILTER_DISCARD:
1262             read_buf.reset();
1263         }
1264 
1265         spice_assert(agent_dev->priv->receive_len);
1266         agent_dev->priv->message_receive_len += agent_dev->priv->receive_len;
1267         agent_dev->priv->read_state = VDI_PORT_READ_STATE_GET_BUFF;
1268         agent_dev->priv->receive_pos = nullptr;
1269     }
1270 }
1271 
reds_marshall_migrate_data(RedsState * reds,SpiceMarshaller * m)1272 void reds_marshall_migrate_data(RedsState *reds, SpiceMarshaller *m)
1273 {
1274     SpiceMigrateDataMain mig_data;
1275     RedCharDeviceVDIPort *agent_dev = reds->agent_dev.get();
1276     SpiceMarshaller *m2;
1277 
1278     memset(&mig_data, 0, sizeof(mig_data));
1279     spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_MAIN_MAGIC);
1280     spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_MAIN_VERSION);
1281 
1282     if (!reds->vdagent) {
1283         uint8_t *null_agent_mig_data;
1284 
1285         /* MSG_AGENT_CONNECTED_TOKENS is supported by the client
1286            (see spice_server_migrate_connect), so agent_attached
1287            is set to FALSE when the agent is disconnected and
1288            there is no need to track the client tokens
1289            (see reds_reset_vdp) */
1290         spice_assert(!agent_dev->priv->agent_attached);
1291         RedCharDevice::migrate_data_marshall_empty(m);
1292         size_t padding_len = sizeof(SpiceMigrateDataMain) - sizeof(SpiceMigrateDataCharDevice);
1293         null_agent_mig_data = spice_marshaller_reserve_space(m, padding_len);
1294         memset(null_agent_mig_data, 0, padding_len);
1295         return;
1296     }
1297 
1298     agent_dev->migrate_data_marshall(m);
1299     spice_marshaller_add_uint8(m, agent_dev->priv->client_agent_started);
1300 
1301     mig_data.agent2client.chunk_header = agent_dev->priv->vdi_chunk_header;
1302 
1303     /* agent to client partial msg */
1304     if (agent_dev->priv->read_state == VDI_PORT_READ_STATE_READ_HEADER) {
1305         mig_data.agent2client.chunk_header_size = agent_dev->priv->receive_pos -
1306             (uint8_t *)&agent_dev->priv->vdi_chunk_header;
1307 
1308         mig_data.agent2client.msg_header_done = FALSE;
1309         mig_data.agent2client.msg_header_partial_len = 0;
1310         spice_assert(!agent_dev->priv->read_filter.msg_data_to_read);
1311     } else {
1312         mig_data.agent2client.chunk_header_size = sizeof(VDIChunkHeader);
1313         mig_data.agent2client.chunk_header.size = agent_dev->priv->message_receive_len;
1314         if (agent_dev->priv->read_state == VDI_PORT_READ_STATE_READ_DATA) {
1315             /* in the middle of reading the message header (see reds_on_main_channel_migrate) */
1316             mig_data.agent2client.msg_header_done = FALSE;
1317             mig_data.agent2client.msg_header_partial_len =
1318                 agent_dev->priv->receive_pos - agent_dev->priv->current_read_buf->data;
1319             spice_assert(mig_data.agent2client.msg_header_partial_len < sizeof(VDAgentMessage));
1320             spice_assert(!agent_dev->priv->read_filter.msg_data_to_read);
1321         } else {
1322             mig_data.agent2client.msg_header_done =  TRUE;
1323             mig_data.agent2client.msg_remaining = agent_dev->priv->read_filter.msg_data_to_read;
1324             mig_data.agent2client.msg_filter_result = agent_dev->priv->read_filter.result;
1325         }
1326     }
1327     spice_marshaller_add_uint32(m, mig_data.agent2client.chunk_header_size);
1328     spice_marshaller_add(m,
1329                          (uint8_t *)&mig_data.agent2client.chunk_header,
1330                          sizeof(VDIChunkHeader));
1331     spice_marshaller_add_uint8(m, mig_data.agent2client.msg_header_done);
1332     spice_marshaller_add_uint32(m, mig_data.agent2client.msg_header_partial_len);
1333     m2 = spice_marshaller_get_ptr_submarshaller(m);
1334     spice_marshaller_add(m2, agent_dev->priv->current_read_buf->data,
1335                          mig_data.agent2client.msg_header_partial_len);
1336     spice_marshaller_add_uint32(m, mig_data.agent2client.msg_remaining);
1337     spice_marshaller_add_uint8(m, mig_data.agent2client.msg_filter_result);
1338 
1339     mig_data.client2agent.msg_remaining = agent_dev->priv->write_filter.msg_data_to_read;
1340     mig_data.client2agent.msg_filter_result = agent_dev->priv->write_filter.result;
1341     spice_marshaller_add_uint32(m, mig_data.client2agent.msg_remaining);
1342     spice_marshaller_add_uint8(m, mig_data.client2agent.msg_filter_result);
1343     spice_debug("from agent filter: discard all %d, wait_msg %u, msg_filter_result %d",
1344                 agent_dev->priv->read_filter.discard_all,
1345                 agent_dev->priv->read_filter.msg_data_to_read,
1346                 agent_dev->priv->read_filter.result);
1347     spice_debug("to agent filter: discard all %d, wait_msg %u, msg_filter_result %d",
1348                 agent_dev->priv->write_filter.discard_all,
1349                 agent_dev->priv->write_filter.msg_data_to_read,
1350                 agent_dev->priv->write_filter.result);
1351 }
1352 
reds_agent_state_restore(RedsState * reds,SpiceMigrateDataMain * mig_data)1353 static int reds_agent_state_restore(RedsState *reds, SpiceMigrateDataMain *mig_data)
1354 {
1355     RedCharDeviceVDIPort *agent_dev = reds->agent_dev.get();
1356     uint32_t chunk_header_remaining;
1357 
1358     agent_dev->priv->vdi_chunk_header = mig_data->agent2client.chunk_header;
1359     spice_assert(mig_data->agent2client.chunk_header_size <= sizeof(VDIChunkHeader));
1360     chunk_header_remaining = sizeof(VDIChunkHeader) - mig_data->agent2client.chunk_header_size;
1361     if (chunk_header_remaining) {
1362         agent_dev->priv->read_state = VDI_PORT_READ_STATE_READ_HEADER;
1363         agent_dev->priv->receive_pos = (uint8_t *)&agent_dev->priv->vdi_chunk_header +
1364             mig_data->agent2client.chunk_header_size;
1365         agent_dev->priv->receive_len = chunk_header_remaining;
1366     } else {
1367         agent_dev->priv->message_receive_len = agent_dev->priv->vdi_chunk_header.size;
1368     }
1369 
1370     if (!mig_data->agent2client.msg_header_done) {
1371         uint8_t *partial_msg_header;
1372 
1373         if (!chunk_header_remaining) {
1374             uint32_t cur_buf_size;
1375 
1376             agent_dev->priv->read_state = VDI_PORT_READ_STATE_READ_DATA;
1377             agent_dev->priv->current_read_buf = vdi_port_get_read_buf(agent_dev);
1378             spice_assert(agent_dev->priv->current_read_buf);
1379             partial_msg_header = (uint8_t *)mig_data + mig_data->agent2client.msg_header_ptr -
1380                 sizeof(SpiceMiniDataHeader);
1381             memcpy(agent_dev->priv->current_read_buf->data,
1382                    partial_msg_header,
1383                    mig_data->agent2client.msg_header_partial_len);
1384             agent_dev->priv->receive_pos = agent_dev->priv->current_read_buf->data +
1385                                       mig_data->agent2client.msg_header_partial_len;
1386             cur_buf_size = sizeof(agent_dev->priv->current_read_buf->data) -
1387                            mig_data->agent2client.msg_header_partial_len;
1388             agent_dev->priv->receive_len = MIN(agent_dev->priv->message_receive_len, cur_buf_size);
1389             agent_dev->priv->current_read_buf->len = agent_dev->priv->receive_len +
1390                                                  mig_data->agent2client.msg_header_partial_len;
1391             agent_dev->priv->message_receive_len -= agent_dev->priv->receive_len;
1392         } else {
1393             spice_assert(mig_data->agent2client.msg_header_partial_len == 0);
1394         }
1395     } else {
1396             agent_dev->priv->read_state = VDI_PORT_READ_STATE_GET_BUFF;
1397             agent_dev->priv->current_read_buf.reset();
1398             agent_dev->priv->receive_pos = nullptr;
1399             agent_dev->priv->read_filter.msg_data_to_read = mig_data->agent2client.msg_remaining;
1400             agent_dev->priv->read_filter.result = (AgentMsgFilterResult) mig_data->agent2client.msg_filter_result;
1401     }
1402 
1403     agent_dev->priv->read_filter.discard_all = FALSE;
1404     agent_dev->priv->write_filter.discard_all = !mig_data->client_agent_started;
1405     agent_dev->priv->client_agent_started = mig_data->client_agent_started;
1406 
1407     agent_dev->priv->write_filter.msg_data_to_read = mig_data->client2agent.msg_remaining;
1408     agent_dev->priv->write_filter.result = (AgentMsgFilterResult) mig_data->client2agent.msg_filter_result;
1409 
1410     spice_debug("to agent filter: discard all %d, wait_msg %u, msg_filter_result %d",
1411                 agent_dev->priv->write_filter.discard_all,
1412                 agent_dev->priv->write_filter.msg_data_to_read,
1413                 agent_dev->priv->write_filter.result);
1414     spice_debug("from agent filter: discard all %d, wait_msg %u, msg_filter_result %d",
1415                 agent_dev->priv->read_filter.discard_all,
1416                 agent_dev->priv->read_filter.msg_data_to_read,
1417                 agent_dev->priv->read_filter.result);
1418     return agent_dev->restore(&mig_data->agent_base);
1419 }
1420 
1421 /*
1422  * The agent device is not attached to the dest before migration is completed. It is
1423  * attached only after the vm is started. It might be attached before or after
1424  * the migration data has reached the server.
1425  */
reds_handle_migrate_data(RedsState * reds,MainChannelClient * mcc,SpiceMigrateDataMain * mig_data,uint32_t size)1426 bool reds_handle_migrate_data(RedsState *reds, MainChannelClient *mcc,
1427                               SpiceMigrateDataMain *mig_data, uint32_t size)
1428 {
1429     RedCharDeviceVDIPort *agent_dev = reds->agent_dev.get();
1430 
1431     spice_debug("main-channel: got migrate data");
1432     /*
1433      * Now that the client has switched to the target server, if main_channel
1434      * controls the mm-time, we update the client's mm-time.
1435      * (MSG_MAIN_INIT is not sent for a migrating connection)
1436      */
1437     if (reds->mm_time_enabled) {
1438         reds_send_mm_time(reds);
1439     }
1440     if (mig_data->agent_base.connected) {
1441         if (agent_dev->priv->agent_attached) { // agent was attached before migration data has arrived
1442             if (!reds->vdagent) {
1443                 spice_assert(agent_dev->priv->plug_generation > 0);
1444                 reds->main_channel->push_agent_disconnected();
1445                 spice_debug("agent is no longer connected");
1446             } else {
1447                 if (agent_dev->priv->plug_generation > 1) {
1448                     /* red_char_device_state_reset takes care of not making the device wait for migration data */
1449                     spice_debug("agent has been detached and reattached before receiving migration data");
1450                     reds->main_channel->push_agent_disconnected();
1451                     reds->main_channel->push_agent_connected();
1452                 } else {
1453                     spice_debug("restoring state from mig_data");
1454                     return reds_agent_state_restore(reds, mig_data);
1455                 }
1456             }
1457         } else {
1458             /* restore agent state when the agent gets attached */
1459             spice_debug("saving mig_data");
1460             spice_assert(agent_dev->priv->plug_generation == 0);
1461             agent_dev->priv->mig_data = (SpiceMigrateDataMain*) g_memdup(mig_data, size);
1462         }
1463     } else {
1464         spice_debug("agent was not attached on the source host");
1465         if (reds->vdagent) {
1466             auto client_opaque =
1467                 (RedCharDeviceClientOpaque *) mcc->get_client();
1468             /* red_char_device_client_remove disables waiting for migration data */
1469             agent_dev->client_remove(client_opaque);
1470             reds->main_channel->push_agent_connected();
1471         }
1472     }
1473 
1474     return TRUE;
1475 }
1476 
reds_channel_init_auth_caps(RedLinkInfo * link,RedChannel * channel)1477 static void reds_channel_init_auth_caps(RedLinkInfo *link, RedChannel *channel)
1478 {
1479     RedsState *reds = link->reds;
1480     if (reds->config->sasl_enabled && !link->skip_auth) {
1481         channel->set_common_cap(SPICE_COMMON_CAP_AUTH_SASL);
1482     } else {
1483         channel->set_common_cap(SPICE_COMMON_CAP_AUTH_SPICE);
1484     }
1485 }
1486 
1487 
red_link_info_get_caps(const RedLinkInfo * link)1488 static const uint32_t *red_link_info_get_caps(const RedLinkInfo *link)
1489 {
1490     const auto caps_start = (const uint8_t *)link->link_mess;
1491 
1492     return (const uint32_t *)(caps_start + link->link_mess->caps_offset);
1493 }
1494 
red_link_info_test_capability(const RedLinkInfo * link,uint32_t cap)1495 static bool red_link_info_test_capability(const RedLinkInfo *link, uint32_t cap)
1496 {
1497     const uint32_t *caps = red_link_info_get_caps(link);
1498 
1499     return test_capability(caps, link->link_mess->num_common_caps, cap);
1500 }
1501 
1502 
reds_send_link_ack(RedsState * reds,RedLinkInfo * link)1503 static bool reds_send_link_ack(RedsState *reds, RedLinkInfo *link)
1504 {
1505     struct {
1506         SpiceLinkHeader header;
1507         SpiceLinkReply ack;
1508     } msg;
1509     RedChannel *channel;
1510     const RedChannelCapabilities *channel_caps;
1511     BUF_MEM *bmBuf;
1512     BIO *bio = nullptr;
1513     int ret = FALSE;
1514     size_t hdr_size;
1515 
1516     SPICE_VERIFY(sizeof(msg) == sizeof(SpiceLinkHeader) + sizeof(SpiceLinkReply));
1517 
1518     msg.header.magic = SPICE_MAGIC;
1519     hdr_size = sizeof(msg.ack);
1520     msg.header.major_version = GUINT32_TO_LE(SPICE_VERSION_MAJOR);
1521     msg.header.minor_version = GUINT32_TO_LE(SPICE_VERSION_MINOR);
1522 
1523     msg.ack.error = GUINT32_TO_LE(SPICE_LINK_ERR_OK);
1524 
1525     channel = reds_find_channel(reds, link->link_mess->channel_type,
1526                                 link->link_mess->channel_id);
1527     if (!channel) {
1528         if (link->link_mess->channel_type != SPICE_CHANNEL_MAIN) {
1529             spice_warning("Received wrong header: channel_type != SPICE_CHANNEL_MAIN");
1530             return FALSE;
1531         }
1532         spice_assert(reds->main_channel);
1533         channel = reds->main_channel.get();
1534     }
1535 
1536     reds_channel_init_auth_caps(link, channel); /* make sure common caps are set */
1537 
1538     channel_caps = channel->get_local_capabilities();
1539     msg.ack.num_common_caps = GUINT32_TO_LE(channel_caps->num_common_caps);
1540     msg.ack.num_channel_caps = GUINT32_TO_LE(channel_caps->num_caps);
1541     hdr_size += channel_caps->num_common_caps * sizeof(uint32_t);
1542     hdr_size += channel_caps->num_caps * sizeof(uint32_t);
1543     msg.header.size = GUINT32_TO_LE(hdr_size);
1544     msg.ack.caps_offset = GUINT32_TO_LE(sizeof(SpiceLinkReply));
1545     if (!reds->config->sasl_enabled
1546         || !red_link_info_test_capability(link, SPICE_COMMON_CAP_AUTH_SASL)) {
1547         if (!(link->tiTicketing.rsa = RSA_new())) {
1548             spice_warning("RSA new failed");
1549             red_dump_openssl_errors();
1550             return FALSE;
1551         }
1552 
1553         if (!(bio = BIO_new(BIO_s_mem()))) {
1554             spice_warning("BIO new failed");
1555             red_dump_openssl_errors();
1556             return FALSE;
1557         }
1558 
1559         if (RSA_generate_key_ex(link->tiTicketing.rsa,
1560                                 SPICE_TICKET_KEY_PAIR_LENGTH,
1561                                 link->tiTicketing.bn,
1562                                 nullptr) != 1) {
1563             spice_warning("Failed to generate %d bits RSA key",
1564                           SPICE_TICKET_KEY_PAIR_LENGTH);
1565             red_dump_openssl_errors();
1566             goto end;
1567         }
1568         link->tiTicketing.rsa_size = RSA_size(link->tiTicketing.rsa);
1569 
1570         i2d_RSA_PUBKEY_bio(bio, link->tiTicketing.rsa);
1571         BIO_get_mem_ptr(bio, &bmBuf);
1572         memcpy(msg.ack.pub_key, bmBuf->data, sizeof(msg.ack.pub_key));
1573     } else {
1574         /* if the client sets the AUTH_SASL cap, it indicates that it
1575          * supports SASL, and will use it if the server supports SASL as
1576          * well. Moreover, a client setting the AUTH_SASL cap also
1577          * indicates that it will not try using the RSA-related content
1578          * in the SpiceLinkReply message, so we don't need to initialize
1579          * it. Reason to avoid this is to fix auth in fips mode where
1580          * the generation of a 1024 bit RSA key as we are trying to do
1581          * will fail.
1582          */
1583         spice_warning("not initialising RSA key");
1584         memset(msg.ack.pub_key, '\0', sizeof(msg.ack.pub_key));
1585     }
1586 
1587     if (!red_stream_write_all(link->stream, &msg, sizeof(msg)))
1588         goto end;
1589     for (unsigned int i = 0; i < channel_caps->num_common_caps; i++) {
1590         guint32 cap = GUINT32_TO_LE(channel_caps->common_caps[i]);
1591         if (!red_stream_write_all(link->stream, &cap, sizeof(cap)))
1592             goto end;
1593     }
1594     for (unsigned int i = 0; i < channel_caps->num_caps; i++) {
1595         guint32 cap = GUINT32_TO_LE(channel_caps->caps[i]);
1596         if (!red_stream_write_all(link->stream, &cap, sizeof(cap)))
1597             goto end;
1598     }
1599 
1600     ret = TRUE;
1601 
1602 end:
1603     if (bio != nullptr)
1604         BIO_free(bio);
1605     return ret;
1606 }
1607 
reds_send_link_error(RedLinkInfo * link,uint32_t error)1608 static bool reds_send_link_error(RedLinkInfo *link, uint32_t error)
1609 {
1610     struct {
1611         SpiceLinkHeader header;
1612         SpiceLinkReply reply;
1613     } msg;
1614     SPICE_VERIFY(sizeof(msg) == sizeof(SpiceLinkHeader) + sizeof(SpiceLinkReply));
1615 
1616     msg.header.magic = SPICE_MAGIC;
1617     msg.header.size = GUINT32_TO_LE(sizeof(msg.reply));
1618     msg.header.major_version = GUINT32_TO_LE(SPICE_VERSION_MAJOR);
1619     msg.header.minor_version = GUINT32_TO_LE(SPICE_VERSION_MINOR);
1620     memset(&msg.reply, 0, sizeof(msg.reply));
1621     msg.reply.error = GUINT32_TO_LE(error);
1622     return red_stream_write_all(link->stream, &msg, sizeof(msg));
1623 }
1624 
reds_info_new_channel(RedLinkInfo * link,int connection_id)1625 static void reds_info_new_channel(RedLinkInfo *link, int connection_id)
1626 {
1627     spice_debug("channel %d:%d, connected successfully, over %s link",
1628                 link->link_mess->channel_type,
1629                 link->link_mess->channel_id,
1630                 red_stream_is_ssl(link->stream) ? "Secure" : "Non Secure");
1631     /* add info + send event */
1632     red_stream_set_channel(link->stream, connection_id,
1633                            link->link_mess->channel_type,
1634                            link->link_mess->channel_id);
1635     red_stream_push_channel_event(link->stream, SPICE_CHANNEL_EVENT_INITIALIZED);
1636 }
1637 
reds_send_link_result(RedLinkInfo * link,uint32_t error)1638 static void reds_send_link_result(RedLinkInfo *link, uint32_t error)
1639 {
1640     error = GUINT32_TO_LE(error);
1641     red_stream_write_all(link->stream, &error, sizeof(error));
1642 }
1643 
reds_mig_target_client_add(RedsState * reds,RedClient * client)1644 static void reds_mig_target_client_add(RedsState *reds, RedClient *client)
1645 {
1646     RedsMigTargetClient *mig_client;
1647 
1648     g_return_if_fail(reds);
1649     spice_debug("trace");
1650     mig_client = g_new0(RedsMigTargetClient, 1);
1651     mig_client->client = client;
1652     reds->mig_target_clients = g_list_append(reds->mig_target_clients, mig_client);
1653 }
1654 
reds_mig_target_client_find(RedsState * reds,RedClient * client)1655 static RedsMigTargetClient* reds_mig_target_client_find(RedsState *reds, RedClient *client)
1656 {
1657     GList *l;
1658 
1659     for (l = reds->mig_target_clients; l != nullptr; l = l->next) {
1660         auto mig_client = (RedsMigTargetClient*) l->data;
1661 
1662         if (mig_client->client == client) {
1663             return mig_client;
1664         }
1665     }
1666     return nullptr;
1667 }
1668 
reds_mig_target_client_add_pending_link(RedsMigTargetClient * client,SpiceLinkMess * link_msg,RedStream * stream)1669 static void reds_mig_target_client_add_pending_link(RedsMigTargetClient *client,
1670                                                     SpiceLinkMess *link_msg,
1671                                                     RedStream *stream)
1672 {
1673     RedsMigPendingLink *mig_link;
1674 
1675     spice_assert(client);
1676     mig_link = g_new0(RedsMigPendingLink, 1);
1677     mig_link->link_msg = link_msg;
1678     mig_link->stream = stream;
1679 
1680     client->pending_links = g_list_append(client->pending_links, mig_link);
1681 }
1682 
reds_mig_target_client_free(RedsState * reds,RedsMigTargetClient * mig_client)1683 static void reds_mig_target_client_free(RedsState *reds, RedsMigTargetClient *mig_client)
1684 {
1685     reds->mig_target_clients = g_list_remove(reds->mig_target_clients, mig_client);
1686     g_list_free_full(mig_client->pending_links, g_free);
1687     g_free(mig_client);
1688 }
1689 
reds_mig_target_client_disconnect_all(RedsState * reds)1690 static void reds_mig_target_client_disconnect_all(RedsState *reds)
1691 {
1692     RedsMigTargetClient *mig_client;
1693 
1694     GLIST_FOREACH(reds->mig_target_clients, RedsMigTargetClient, mig_client) {
1695         reds_client_disconnect(reds, mig_client->client);
1696     }
1697 }
1698 
reds_find_client(RedsState * reds,RedClient * client)1699 static bool reds_find_client(RedsState *reds, RedClient *client)
1700 {
1701     for (auto list_client: reds->clients) {
1702         if (list_client == client) {
1703             return TRUE;
1704         }
1705     }
1706     return FALSE;
1707 }
1708 
1709 /* should be used only when there is one client */
reds_get_client(RedsState * reds)1710 static RedClient *reds_get_client(RedsState *reds)
1711 {
1712     spice_assert(reds->clients.size() <= 1);
1713 
1714     if (reds->clients.empty()) {
1715         return nullptr;
1716     }
1717 
1718     return *reds->clients.begin();
1719 }
1720 
1721 /* Performs late initializations steps.
1722  * This should be called when a client connects */
reds_late_initialization(RedsState * reds)1723 static void reds_late_initialization(RedsState *reds)
1724 {
1725     // do only once
1726     if (reds->late_initialization_done) {
1727         return;
1728     }
1729 
1730     // create stream channels for streaming devices
1731     for (const auto& dev: reds->char_devices) {
1732         auto stream_dev = dynamic_cast<StreamDevice*>(dev.get());
1733         if (stream_dev) {
1734             stream_dev->create_channel();
1735         }
1736     }
1737     reds->late_initialization_done = true;
1738 }
1739 
1740 static void
red_channel_capabilities_init_from_link_message(RedChannelCapabilities * caps,const SpiceLinkMess * link_mess)1741 red_channel_capabilities_init_from_link_message(RedChannelCapabilities *caps,
1742                                                 const SpiceLinkMess *link_mess)
1743 {
1744     const uint8_t *raw_caps = (const uint8_t *)link_mess + link_mess->caps_offset;
1745 
1746     caps->num_common_caps = link_mess->num_common_caps;
1747     caps->common_caps = nullptr;
1748     if (caps->num_common_caps) {
1749         caps->common_caps = (uint32_t*) g_memdup(raw_caps,
1750                                      link_mess->num_common_caps * sizeof(uint32_t));
1751     }
1752     caps->num_caps = link_mess->num_channel_caps;
1753     caps->caps = nullptr;
1754     if (link_mess->num_channel_caps) {
1755         caps->caps = (uint32_t*) g_memdup(raw_caps + link_mess->num_common_caps * sizeof(uint32_t),
1756                               link_mess->num_channel_caps * sizeof(uint32_t));
1757     }
1758 }
1759 
1760 // TODO: now that main is a separate channel this should
1761 // actually be joined with reds_handle_other_links, become reds_handle_link
reds_handle_main_link(RedsState * reds,RedLinkInfo * link)1762 static void reds_handle_main_link(RedsState *reds, RedLinkInfo *link)
1763 {
1764     RedClient *client;
1765     RedStream *stream;
1766     SpiceLinkMess *link_mess;
1767     uint32_t connection_id;
1768     MainChannelClient *mcc;
1769     int mig_target = FALSE;
1770     RedChannelCapabilities caps;
1771 
1772     spice_debug("trace");
1773     spice_assert(reds->main_channel);
1774 
1775     reds_late_initialization(reds);
1776 
1777     link_mess = link->link_mess;
1778     if (!reds->allow_multiple_clients) {
1779         reds_disconnect(reds);
1780     }
1781 
1782     if (link_mess->connection_id == 0) {
1783         reds_send_link_result(link, SPICE_LINK_ERR_OK);
1784         while((connection_id = rand()) == 0);
1785         mig_target = FALSE;
1786     } else {
1787         // TODO: make sure link_mess->connection_id is the same
1788         // connection id the migration src had (use vmstate to store the connection id)
1789         reds_send_link_result(link, SPICE_LINK_ERR_OK);
1790         connection_id = link_mess->connection_id;
1791         mig_target = TRUE;
1792     }
1793 
1794     reds->mig_inprogress = FALSE;
1795     reds->mig_wait_connect = FALSE;
1796     reds->mig_wait_disconnect = FALSE;
1797 
1798     reds_info_new_channel(link, connection_id);
1799     stream = link->stream;
1800     link->stream = nullptr;
1801     client = red_client_new(reds, mig_target);
1802     reds->clients.push_front(client);
1803 
1804     red_channel_capabilities_init_from_link_message(&caps, link_mess);
1805     mcc = main_channel_link(reds->main_channel.get(), client,
1806                             stream, connection_id, mig_target,
1807                             &caps);
1808     red_channel_capabilities_reset(&caps);
1809     spice_debug("NEW Client %p mcc %p connect-id %d", client, mcc, connection_id);
1810 
1811     if (reds->vdagent) {
1812         if (mig_target) {
1813             spice_warning("unexpected: vdagent attached to destination during migration");
1814         }
1815         agent_msg_filter_config(&reds->agent_dev->priv->read_filter,
1816                                 reds->config->agent_copypaste,
1817                                 reds->config->agent_file_xfer,
1818                                 reds_use_client_monitors_config(reds));
1819         reds->agent_dev->priv->read_filter.discard_all = FALSE;
1820         reds->agent_dev->priv->plug_generation++;
1821     }
1822 
1823     if (!mig_target) {
1824         mcc->push_init(reds->qxl_instances.size(), reds->mouse_mode,
1825                        reds->is_client_mouse_allowed,
1826                        reds_get_mm_time() - MM_TIME_DELTA,
1827                        reds_qxl_ram_size(reds));
1828         if (reds->config->spice_name)
1829             mcc->push_name(reds->config->spice_name);
1830         if (reds->config->spice_uuid_is_set)
1831             mcc->push_uuid(reds->config->spice_uuid);
1832     } else {
1833         reds_mig_target_client_add(reds, client);
1834     }
1835 
1836     if (red_stream_get_family(stream) != AF_UNIX) {
1837         mcc->start_net_test(!mig_target);
1838     }
1839 }
1840 
openssl_init(RedLinkInfo * link)1841 static void openssl_init(RedLinkInfo *link)
1842 {
1843     unsigned long f4 = RSA_F4;
1844     link->tiTicketing.bn = BN_new();
1845 
1846     if (!link->tiTicketing.bn) {
1847         red_dump_openssl_errors();
1848         spice_error("OpenSSL BIGNUMS alloc failed");
1849     }
1850 
1851     BN_set_word(link->tiTicketing.bn, f4);
1852 }
1853 
reds_channel_do_link(RedChannel * channel,RedClient * client,SpiceLinkMess * link_msg,RedStream * stream)1854 static void reds_channel_do_link(RedChannel *channel, RedClient *client,
1855                                  SpiceLinkMess *link_msg,
1856                                  RedStream *stream)
1857 {
1858     RedChannelCapabilities caps;
1859 
1860     spice_assert(channel);
1861     spice_assert(link_msg);
1862     spice_assert(stream);
1863 
1864     red_channel_capabilities_init_from_link_message(&caps, link_msg);
1865     channel->connect(client, stream,
1866                      client->during_migrate_at_target(), &caps);
1867     red_channel_capabilities_reset(&caps);
1868 }
1869 
1870 /*
1871  * migration target side:
1872  * In semi-seamless migration, we activate the channels only
1873  * after migration is completed.
1874  * In seamless migration, in order to keep the continuousness, and
1875  * not lose any data, we activate the target channels before
1876  * migration completes, as soon as we receive SPICE_MSGC_MAIN_MIGRATE_DST_DO_SEAMLESS
1877  */
reds_link_mig_target_channels(RedsState * reds,RedClient * client)1878 static bool reds_link_mig_target_channels(RedsState *reds, RedClient *client)
1879 {
1880     RedsMigTargetClient *mig_client;
1881     GList *item;
1882 
1883     spice_debug("%p", client);
1884     mig_client = reds_mig_target_client_find(reds, client);
1885     if (!mig_client) {
1886         spice_debug("Error: mig target client was not found");
1887         return FALSE;
1888     }
1889 
1890     /* Each channel should check if we are during migration, and
1891      * act accordingly. */
1892     for(item = mig_client->pending_links; item != nullptr; item = item->next) {
1893         auto mig_link = (RedsMigPendingLink*) item->data;
1894         RedChannel *channel;
1895 
1896         channel = reds_find_channel(reds, mig_link->link_msg->channel_type,
1897                                     mig_link->link_msg->channel_id);
1898         if (!channel) {
1899             spice_warning("client %p channel (%d, %d) (type, id) wasn't found",
1900                           client,
1901                           mig_link->link_msg->channel_type,
1902                           mig_link->link_msg->channel_id);
1903             continue;
1904         }
1905         reds_channel_do_link(channel, client, mig_link->link_msg, mig_link->stream);
1906     }
1907 
1908     reds_mig_target_client_free(reds, mig_client);
1909 
1910     return TRUE;
1911 }
1912 
reds_on_migrate_dst_set_seamless(RedsState * reds,MainChannelClient * mcc,uint32_t src_version)1913 int reds_on_migrate_dst_set_seamless(RedsState *reds, MainChannelClient *mcc, uint32_t src_version)
1914 {
1915     /* seamless migration is not supported with multiple clients*/
1916     if (reds->allow_multiple_clients  || src_version > SPICE_MIGRATION_PROTOCOL_VERSION) {
1917         reds->dst_do_seamless_migrate = FALSE;
1918     } else {
1919         RedClient *client = mcc->get_client();
1920 
1921         client->set_migration_seamless();
1922         /* linking all the channels that have been connected before migration handshake */
1923         reds->dst_do_seamless_migrate = reds_link_mig_target_channels(reds, client);
1924     }
1925     return reds->dst_do_seamless_migrate;
1926 }
1927 
reds_on_client_seamless_migrate_complete(RedsState * reds,RedClient * client)1928 void reds_on_client_seamless_migrate_complete(RedsState *reds, RedClient *client)
1929 {
1930     spice_debug("trace");
1931     if (!reds_find_client(reds, client)) {
1932         spice_debug("client no longer exists");
1933         return;
1934     }
1935     client->get_main()->migrate_dst_complete();
1936 }
1937 
reds_on_client_semi_seamless_migrate_complete(RedsState * reds,RedClient * client)1938 void reds_on_client_semi_seamless_migrate_complete(RedsState *reds, RedClient *client)
1939 {
1940     MainChannelClient *mcc;
1941 
1942     spice_debug("%p", client);
1943     mcc = client->get_main();
1944 
1945     // TODO: not doing net test. consider doing it on client_migrate_info
1946     mcc->push_init(reds->qxl_instances.size(), reds->mouse_mode,
1947                    reds->is_client_mouse_allowed,
1948                    reds_get_mm_time() - MM_TIME_DELTA,
1949                    reds_qxl_ram_size(reds));
1950     reds_link_mig_target_channels(reds, client);
1951     mcc->migrate_dst_complete();
1952 }
1953 
reds_handle_other_links(RedsState * reds,RedLinkInfo * link)1954 static void reds_handle_other_links(RedsState *reds, RedLinkInfo *link)
1955 {
1956     RedChannel *channel;
1957     RedClient *client = nullptr;
1958     SpiceLinkMess *link_mess;
1959     RedsMigTargetClient *mig_client;
1960 
1961     link_mess = link->link_mess;
1962     if (reds->main_channel) {
1963         client = reds->main_channel->get_client_by_link_id(link_mess->connection_id);
1964     }
1965 
1966     // TODO: MC: broke migration (at least for the dont-drop-connection kind).
1967     // On migration we should get a connection_id to expect (must be a security measure)
1968     // where do we store it? on reds, but should be a list (MC).
1969     if (!client) {
1970         reds_send_link_result(link, SPICE_LINK_ERR_BAD_CONNECTION_ID);
1971         return;
1972     }
1973 
1974     // TODO: MC: be less lenient. Tally connections from same connection_id (by same client).
1975     if (!(channel = reds_find_channel(reds, link_mess->channel_type,
1976                                       link_mess->channel_id))) {
1977         reds_send_link_result(link, SPICE_LINK_ERR_CHANNEL_NOT_AVAILABLE);
1978         return;
1979     }
1980 
1981     reds_send_link_result(link, SPICE_LINK_ERR_OK);
1982     reds_info_new_channel(link, link_mess->connection_id);
1983 
1984     mig_client = reds_mig_target_client_find(reds, client);
1985     /*
1986      * In semi-seamless migration, we activate the channels only
1987      * after migration is completed. Since, the session starts almost from
1988      * scratch we don't mind if we skip some messages in between the src session end and
1989      * dst session start.
1990      * In seamless migration, in order to keep the continuousness of the session, and
1991      * in order not to lose any data, we activate the target channels before
1992      * migration completes, as soon as we receive SPICE_MSGC_MAIN_MIGRATE_DST_DO_SEAMLESS.
1993      * If a channel connects before receiving SPICE_MSGC_MAIN_MIGRATE_DST_DO_SEAMLESS,
1994      * reds_on_migrate_dst_set_seamless will take care of activating it */
1995     if (client->during_migrate_at_target() && !reds->dst_do_seamless_migrate) {
1996         spice_assert(mig_client);
1997         reds_mig_target_client_add_pending_link(mig_client, link_mess, link->stream);
1998         link->link_mess = nullptr;
1999     } else {
2000         spice_assert(!mig_client);
2001         reds_channel_do_link(channel, client, link_mess, link->stream);
2002     }
2003     link->stream = nullptr;
2004 }
2005 
reds_handle_link(RedLinkInfo * link)2006 static void reds_handle_link(RedLinkInfo *link)
2007 {
2008     RedsState *reds = link->reds;
2009 
2010     red_stream_remove_watch(link->stream);
2011     if (link->link_mess->channel_type == SPICE_CHANNEL_MAIN) {
2012         reds_handle_main_link(reds, link);
2013     } else {
2014         reds_handle_other_links(reds, link);
2015     }
2016     reds_link_free(link);
2017 }
2018 
reds_handle_ticket(void * opaque)2019 static void reds_handle_ticket(void *opaque)
2020 {
2021     auto link = (RedLinkInfo *)opaque;
2022     RedsState *reds = link->reds;
2023     char *password;
2024     int password_size;
2025 
2026     if (RSA_size(link->tiTicketing.rsa) < SPICE_MAX_PASSWORD_LENGTH) {
2027         spice_warning("RSA modulus size is smaller than SPICE_MAX_PASSWORD_LENGTH (%d < %d), "
2028                       "SPICE ticket sent from client may be truncated",
2029                       RSA_size(link->tiTicketing.rsa), SPICE_MAX_PASSWORD_LENGTH);
2030     }
2031 
2032     password = (char *) alloca(RSA_size(link->tiTicketing.rsa) + 1);
2033     password_size = RSA_private_decrypt(link->tiTicketing.rsa_size,
2034                                         link->tiTicketing.encrypted_ticket.encrypted_data,
2035                                         (unsigned char *)password,
2036                                         link->tiTicketing.rsa,
2037                                         RSA_PKCS1_OAEP_PADDING);
2038     if (password_size == -1) {
2039         spice_warning("failed to decrypt RSA encrypted password");
2040         red_dump_openssl_errors();
2041         goto error;
2042     }
2043     password[password_size] = '\0';
2044 
2045     if (reds->config->ticketing_enabled && !link->skip_auth) {
2046         time_t ltime;
2047         bool expired;
2048 
2049         if (strlen(reds->config->taTicket.password) == 0) {
2050             spice_warning("Ticketing is enabled, but no password is set. "
2051                           "please set a ticket first");
2052             goto error;
2053         }
2054 
2055         ltime = spice_get_monotonic_time_ns() / NSEC_PER_SEC;
2056         expired = (reds->config->taTicket.expiration_time < ltime);
2057 
2058         if (expired) {
2059             spice_warning("Ticket has expired");
2060             goto error;
2061         }
2062 
2063         if (strcmp(password, reds->config->taTicket.password) != 0) {
2064             spice_warning("Invalid password");
2065             goto error;
2066         }
2067     }
2068 
2069     reds_handle_link(link);
2070     return;
2071 
2072 error:
2073     reds_send_link_result(link, SPICE_LINK_ERR_PERMISSION_DENIED);
2074     reds_link_free(link);
2075 }
2076 
reds_get_spice_ticket(RedLinkInfo * link)2077 static void reds_get_spice_ticket(RedLinkInfo *link)
2078 {
2079     red_stream_async_read(link->stream,
2080                           (uint8_t *)&link->tiTicketing.encrypted_ticket.encrypted_data,
2081                           link->tiTicketing.rsa_size, reds_handle_ticket, link);
2082 }
2083 
2084 #if HAVE_SASL
reds_handle_sasl_result(void * opaque,RedSaslError status)2085 static void reds_handle_sasl_result(void *opaque, RedSaslError status)
2086 {
2087     RedLinkInfo *link = (RedLinkInfo *)opaque;
2088 
2089     switch (status) {
2090     case RED_SASL_ERROR_OK:
2091         reds_handle_link(link);
2092         break;
2093     case RED_SASL_ERROR_INVALID_DATA:
2094         reds_send_link_error(link, SPICE_LINK_ERR_INVALID_DATA);
2095         reds_link_free(link);
2096         break;
2097     default:
2098         // in these cases error was reported using SASL protocol
2099         // (RED_SASL_ERROR_AUTH_FAILED) or we just need to close the
2100         // connection
2101         reds_link_free(link);
2102         break;
2103     }
2104 }
2105 
reds_start_auth_sasl(RedLinkInfo * link)2106 static void reds_start_auth_sasl(RedLinkInfo *link)
2107 {
2108     if (!red_sasl_start_auth(link->stream, reds_handle_sasl_result, link)) {
2109         reds_link_free(link);
2110     }
2111 }
2112 #endif
2113 
reds_handle_auth_mechanism(void * opaque)2114 static void reds_handle_auth_mechanism(void *opaque)
2115 {
2116     auto link = (RedLinkInfo *)opaque;
2117     RedsState *reds = link->reds;
2118 
2119     spice_debug("Auth method: %d", link->auth_mechanism.auth_mechanism);
2120 
2121     link->auth_mechanism.auth_mechanism = GUINT32_FROM_LE(link->auth_mechanism.auth_mechanism);
2122     if (link->auth_mechanism.auth_mechanism == SPICE_COMMON_CAP_AUTH_SPICE
2123         && !reds->config->sasl_enabled
2124         ) {
2125         reds_get_spice_ticket(link);
2126 #if HAVE_SASL
2127     } else if (link->auth_mechanism.auth_mechanism == SPICE_COMMON_CAP_AUTH_SASL) {
2128         spice_debug("Starting SASL");
2129         reds_start_auth_sasl(link);
2130 #endif
2131     } else {
2132         spice_warning("Unknown auth method, disconnecting");
2133         if (reds->config->sasl_enabled) {
2134             spice_warning("Your client doesn't handle SASL?");
2135         }
2136         reds_send_link_error(link, SPICE_LINK_ERR_INVALID_DATA);
2137         reds_link_free(link);
2138     }
2139 }
2140 
reds_security_check(RedLinkInfo * link)2141 static int reds_security_check(RedLinkInfo *link)
2142 {
2143     RedsState *reds = link->reds;
2144     ChannelSecurityOptions *security_option = reds_find_channel_security(reds, link->link_mess->channel_type);
2145     uint32_t security = security_option ? security_option->options : reds->config->default_channel_security;
2146     return (red_stream_is_ssl(link->stream) && (security & SPICE_CHANNEL_SECURITY_SSL)) ||
2147         (!red_stream_is_ssl(link->stream) && (security & SPICE_CHANNEL_SECURITY_NONE));
2148 }
2149 
reds_handle_read_link_done(void * opaque)2150 static void reds_handle_read_link_done(void *opaque)
2151 {
2152     auto link = (RedLinkInfo *)opaque;
2153     RedsState *reds = link->reds;
2154     SpiceLinkMess *link_mess = link->link_mess;
2155     uint32_t num_caps;
2156     uint32_t *caps;
2157     int auth_selection;
2158     unsigned int i;
2159 
2160     link_mess->caps_offset = GUINT32_FROM_LE(link_mess->caps_offset);
2161     link_mess->connection_id = GUINT32_FROM_LE(link_mess->connection_id);
2162     link_mess->num_channel_caps = GUINT32_FROM_LE(link_mess->num_channel_caps);
2163     link_mess->num_common_caps = GUINT32_FROM_LE(link_mess->num_common_caps);
2164 
2165     /* Prevent DoS. Currently we defined only 13 capabilities,
2166      * I expect 1024 to be valid for quite a lot time */
2167     if (link_mess->num_channel_caps > 1024 || link_mess->num_common_caps > 1024) {
2168         reds_send_link_error(link, SPICE_LINK_ERR_INVALID_DATA);
2169         reds_link_free(link);
2170         return;
2171     }
2172 
2173     num_caps = link_mess->num_common_caps + link_mess->num_channel_caps;
2174     caps = (uint32_t *)((uint8_t *)link_mess + link_mess->caps_offset);
2175 
2176     if (num_caps && (num_caps * sizeof(uint32_t) + link_mess->caps_offset >
2177                      link->link_header.size ||
2178                      link_mess->caps_offset < sizeof(*link_mess))) {
2179         reds_send_link_error(link, SPICE_LINK_ERR_INVALID_DATA);
2180         reds_link_free(link);
2181         return;
2182     }
2183 
2184     for(i = 0; i < num_caps;i++)
2185         caps[i] = GUINT32_FROM_LE(caps[i]);
2186 
2187     auth_selection = red_link_info_test_capability(link,
2188                                                    SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION);
2189 
2190     if (!reds_security_check(link)) {
2191         if (red_stream_is_ssl(link->stream)) {
2192             spice_warning("spice channels %d should not be encrypted", link_mess->channel_type);
2193             reds_send_link_error(link, SPICE_LINK_ERR_NEED_UNSECURED);
2194         } else {
2195             spice_warning("spice channels %d should be encrypted", link_mess->channel_type);
2196             reds_send_link_error(link, SPICE_LINK_ERR_NEED_SECURED);
2197         }
2198         reds_link_free(link);
2199         return;
2200     }
2201 
2202     if (!reds_send_link_ack(reds, link)) {
2203         reds_link_free(link);
2204         return;
2205     }
2206 
2207     if (!auth_selection) {
2208         if (reds->config->sasl_enabled && !link->skip_auth) {
2209             spice_warning("SASL enabled, but peer supports only spice authentication");
2210             reds_send_link_error(link, SPICE_LINK_ERR_VERSION_MISMATCH);
2211             return;
2212         }
2213         spice_warning("Peer doesn't support AUTH selection");
2214         reds_get_spice_ticket(link);
2215     } else {
2216         red_stream_async_read(link->stream,
2217                               (uint8_t *)&link->auth_mechanism,
2218                               sizeof(SpiceLinkAuthMechanism),
2219                               reds_handle_auth_mechanism,
2220                               link);
2221     }
2222 }
2223 
reds_handle_link_error(void * opaque,int err)2224 static void reds_handle_link_error(void *opaque, int err)
2225 {
2226     auto link = (RedLinkInfo *)opaque;
2227     switch (err) {
2228     case 0:
2229     case EPIPE:
2230         break;
2231     default:
2232         spice_warning("%s", strerror(errno));
2233         break;
2234     }
2235     reds_link_free(link);
2236 }
2237 
2238 static void reds_handle_new_link(RedLinkInfo *link);
reds_handle_read_header_done(void * opaque)2239 static void reds_handle_read_header_done(void *opaque)
2240 {
2241     auto link = (RedLinkInfo *)opaque;
2242     SpiceLinkHeader *header = &link->link_header;
2243 
2244     header->major_version = GUINT32_FROM_LE(header->major_version);
2245     header->minor_version = GUINT32_FROM_LE(header->minor_version);
2246     header->size = GUINT32_FROM_LE(header->size);
2247 
2248     if (header->major_version != SPICE_VERSION_MAJOR) {
2249         if (header->major_version > 0) {
2250             reds_send_link_error(link, SPICE_LINK_ERR_VERSION_MISMATCH);
2251         }
2252 
2253         spice_warning("version mismatch");
2254         reds_link_free(link);
2255         return;
2256     }
2257 
2258     /* the check for 4096 is to avoid clients to cause arbitrary big memory allocations */
2259     if (header->size < sizeof(SpiceLinkMess) || header->size > 4096) {
2260         reds_send_link_error(link, SPICE_LINK_ERR_INVALID_DATA);
2261         spice_warning("bad size %u", header->size);
2262         reds_link_free(link);
2263         return;
2264     }
2265 
2266     link->link_mess = (SpiceLinkMess*) g_malloc(header->size);
2267 
2268     red_stream_async_read(link->stream,
2269                           (uint8_t *)link->link_mess,
2270                           header->size,
2271                           reds_handle_read_link_done,
2272                           link);
2273 }
2274 
reds_handle_read_magic_done(void * opaque)2275 static void reds_handle_read_magic_done(void *opaque)
2276 {
2277     auto link = (RedLinkInfo *)opaque;
2278     const SpiceLinkHeader *header = &link->link_header;
2279 
2280     if (header->magic != SPICE_MAGIC) {
2281         /* Attempt to detect and support a WebSocket connection,
2282            which will be proceeded by a variable length GET style request.
2283            We cannot know we are dealing with a WebSocket connection
2284            until we have read at least 3 bytes, and we will have to
2285            read many more bytes than are contained in a SpiceLinkHeader.
2286            So we may as well read a SpiceLinkHeader's worth of data, and if it's
2287            clear that a WebSocket connection was requested, we switch
2288            before proceeding further. */
2289         if (red_stream_is_websocket(link->stream, &header->magic, sizeof(header->magic))) {
2290             reds_handle_new_link(link);
2291             return;
2292         }
2293         reds_send_link_error(link, SPICE_LINK_ERR_INVALID_MAGIC);
2294         reds_link_free(link);
2295         return;
2296     }
2297 
2298     red_stream_async_read(link->stream,
2299                           ((uint8_t *)&link->link_header) + sizeof(header->magic),
2300                           sizeof(SpiceLinkHeader) - sizeof(header->magic),
2301                           reds_handle_read_header_done,
2302                           link);
2303 }
2304 
reds_handle_new_link(RedLinkInfo * link)2305 static void reds_handle_new_link(RedLinkInfo *link)
2306 {
2307     red_stream_set_async_error_handler(link->stream, reds_handle_link_error);
2308     red_stream_async_read(link->stream,
2309                           (uint8_t *)&link->link_header,
2310                           sizeof(link->link_header.magic),
2311                           reds_handle_read_magic_done,
2312                           link);
2313 }
2314 
reds_handle_ssl_accept(int fd,int event,void * data)2315 static void reds_handle_ssl_accept(int fd, int event, void *data)
2316 {
2317     auto link = (RedLinkInfo *)data;
2318     RedStreamSslStatus return_code = red_stream_ssl_accept(link->stream);
2319 
2320     switch (return_code) {
2321         case RED_STREAM_SSL_STATUS_ERROR:
2322             reds_link_free(link);
2323             return;
2324         case RED_STREAM_SSL_STATUS_WAIT_FOR_READ:
2325             red_watch_update_mask(link->stream->watch, SPICE_WATCH_EVENT_READ);
2326             return;
2327         case RED_STREAM_SSL_STATUS_WAIT_FOR_WRITE:
2328             red_watch_update_mask(link->stream->watch, SPICE_WATCH_EVENT_WRITE);
2329             return;
2330         case RED_STREAM_SSL_STATUS_OK:
2331             red_stream_remove_watch(link->stream);
2332             reds_handle_new_link(link);
2333     }
2334 }
2335 
2336 #define KEEPALIVE_TIMEOUT (10*60)
2337 
reds_init_client_connection(RedsState * reds,int socket)2338 static RedLinkInfo *reds_init_client_connection(RedsState *reds, int socket)
2339 {
2340     RedLinkInfo *link;
2341 
2342     if (!red_socket_set_non_blocking(socket, TRUE)) {
2343         return nullptr;
2344     }
2345 
2346     if (!red_socket_set_no_delay(socket, TRUE)) {
2347         return nullptr;
2348     }
2349 
2350     red_socket_set_keepalive(socket, TRUE, KEEPALIVE_TIMEOUT);
2351     red_socket_set_nosigpipe(socket, true);
2352 
2353     link = g_new0(RedLinkInfo, 1);
2354     link->reds = reds;
2355     link->stream = red_stream_new(reds, socket);
2356 
2357     /* gather info + send event */
2358 
2359     red_stream_push_channel_event(link->stream, SPICE_CHANNEL_EVENT_CONNECTED);
2360 
2361     openssl_init(link);
2362 
2363     return link;
2364 }
2365 
2366 
reds_init_client_ssl_connection(RedsState * reds,int socket)2367 static RedLinkInfo *reds_init_client_ssl_connection(RedsState *reds, int socket)
2368 {
2369     RedLinkInfo *link;
2370     RedStreamSslStatus ssl_status;
2371 
2372     link = reds_init_client_connection(reds, socket);
2373     if (link == nullptr) {
2374         return nullptr;
2375     }
2376 
2377     ssl_status = red_stream_enable_ssl(link->stream, reds->ctx);
2378     switch (ssl_status) {
2379         case RED_STREAM_SSL_STATUS_OK:
2380             reds_handle_new_link(link);
2381             return link;
2382         case RED_STREAM_SSL_STATUS_ERROR:
2383             goto error;
2384         case RED_STREAM_SSL_STATUS_WAIT_FOR_READ:
2385             link->stream->watch = reds_core_watch_add(reds, link->stream->socket,
2386                                                       SPICE_WATCH_EVENT_READ,
2387                                                       reds_handle_ssl_accept, link);
2388             break;
2389         case RED_STREAM_SSL_STATUS_WAIT_FOR_WRITE:
2390             link->stream->watch = reds_core_watch_add(reds, link->stream->socket,
2391                                                       SPICE_WATCH_EVENT_WRITE,
2392                                                       reds_handle_ssl_accept, link);
2393             break;
2394     }
2395     return link;
2396 
2397 error:
2398     /* close the stream but do not close the socket, this API is
2399      * supposed to not close it if it fails */
2400     link->stream->socket = -1;
2401     reds_link_free(link);
2402     return nullptr;
2403 }
2404 
reds_accept_ssl_connection(int fd,int event,void * data)2405 static void reds_accept_ssl_connection(int fd, int event, void *data)
2406 {
2407     auto reds = (RedsState*) data;
2408     RedLinkInfo *link;
2409     int socket;
2410 
2411     if ((socket = accept(fd, nullptr, nullptr)) == -1) {
2412         spice_warning("accept failed, %s", strerror(errno));
2413         return;
2414     }
2415 
2416     if (!(link = reds_init_client_ssl_connection(reds, socket))) {
2417         socket_close(socket);
2418     }
2419 }
2420 
2421 
reds_accept(int fd,int event,void * data)2422 static void reds_accept(int fd, int event, void *data)
2423 {
2424     auto reds = (RedsState*) data;
2425     int socket;
2426 
2427     if ((socket = accept(fd, nullptr, nullptr)) == -1) {
2428         spice_warning("accept failed, %s", strerror(errno));
2429         return;
2430     }
2431 
2432     if (spice_server_add_client(reds, socket, 0) < 0) {
2433         socket_close(socket);
2434     }
2435 }
2436 
2437 
spice_server_add_client(SpiceServer * reds,int socket,int skip_auth)2438 SPICE_GNUC_VISIBLE int spice_server_add_client(SpiceServer *reds, int socket, int skip_auth)
2439 {
2440     RedLinkInfo *link;
2441 
2442     if (!(link = reds_init_client_connection(reds, socket))) {
2443         spice_warning("accept failed");
2444         return -1;
2445     }
2446 
2447     link->skip_auth = skip_auth;
2448 
2449     reds_handle_new_link(link);
2450     return 0;
2451 }
2452 
2453 
spice_server_add_ssl_client(SpiceServer * reds,int socket,int skip_auth)2454 SPICE_GNUC_VISIBLE int spice_server_add_ssl_client(SpiceServer *reds, int socket, int skip_auth)
2455 {
2456     RedLinkInfo *link;
2457 
2458     if (!(link = reds_init_client_ssl_connection(reds, socket))) {
2459         return -1;
2460     }
2461 
2462     link->skip_auth = skip_auth;
2463     return 0;
2464 }
2465 
2466 
reds_init_socket(const char * addr,int portnr,int family)2467 static int reds_init_socket(const char *addr, int portnr, int family)
2468 {
2469     static const int on=1, off=0;
2470     struct addrinfo ai,*res,*e;
2471     char port[33];
2472     int slisten, rc;
2473 
2474     if (family == AF_UNIX) {
2475 #ifndef _WIN32
2476         int len;
2477         struct sockaddr_un local = { 0, };
2478 
2479         if ((slisten = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
2480             perror("socket");
2481             return -1;
2482         }
2483 
2484         local.sun_family = AF_UNIX;
2485         g_strlcpy(local.sun_path, addr, sizeof(local.sun_path));
2486         len = SUN_LEN(&local);
2487         if (local.sun_path[0] == '@') {
2488             local.sun_path[0] = 0;
2489         } else {
2490             unlink(local.sun_path);
2491         }
2492         if (bind(slisten, (struct sockaddr *)&local, len) == -1) {
2493             perror("bind");
2494             socket_close(slisten);
2495             return -1;
2496         }
2497 
2498         goto listen;
2499 #else
2500         return -1;
2501 #endif
2502     }
2503 
2504     memset(&ai,0, sizeof(ai));
2505     ai.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
2506     ai.ai_socktype = SOCK_STREAM;
2507     ai.ai_family = family;
2508 
2509     snprintf(port, sizeof(port), "%d", portnr);
2510     rc = getaddrinfo(strlen(addr) ? addr : nullptr, port, &ai, &res);
2511     if (rc != 0) {
2512         spice_warning("getaddrinfo(%s,%s): %s", addr, port,
2513                       gai_strerror(rc));
2514         return -1;
2515     }
2516 
2517     for (e = res; e != nullptr; e = e->ai_next) {
2518         slisten = socket(e->ai_family, e->ai_socktype, e->ai_protocol);
2519         if (slisten < 0) {
2520             continue;
2521         }
2522 
2523         setsockopt(slisten,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on));
2524 #ifdef IPV6_V6ONLY
2525         if (e->ai_family == PF_INET6) {
2526             /* listen on both ipv4 and ipv6 */
2527             setsockopt(slisten,IPPROTO_IPV6,IPV6_V6ONLY,(void*)&off,
2528                        sizeof(off));
2529         }
2530 #endif
2531         if (bind(slisten, e->ai_addr, e->ai_addrlen) == 0) {
2532             char uaddr[INET6_ADDRSTRLEN+1];
2533             char uport[33];
2534             rc = getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
2535                              uaddr,INET6_ADDRSTRLEN, uport,32,
2536                              NI_NUMERICHOST | NI_NUMERICSERV);
2537             if (rc == 0) {
2538                 spice_debug("bound to %s:%s", uaddr, uport);
2539             } else {
2540                 spice_debug("cannot resolve address spice-server is bound to");
2541             }
2542             freeaddrinfo(res);
2543             goto listen;
2544         }
2545         socket_close(slisten);
2546     }
2547     spice_warning("binding socket to %s:%d failed", addr, portnr);
2548     freeaddrinfo(res);
2549     return -1;
2550 
2551 listen:
2552     if (listen(slisten, SOMAXCONN) != 0) {
2553         spice_warning("listen: %s", strerror(errno));
2554         socket_close(slisten);
2555         return -1;
2556     }
2557     return slisten;
2558 }
2559 
reds_send_mm_time(RedsState * reds)2560 static void reds_send_mm_time(RedsState *reds)
2561 {
2562     if (!reds_main_channel_connected(reds)) {
2563         return;
2564     }
2565     spice_debug("trace");
2566     reds->main_channel->push_multi_media_time(reds_get_mm_time() - reds->mm_time_latency);
2567 }
2568 
reds_set_client_mm_time_latency(RedsState * reds,RedClient * client,uint32_t latency)2569 void reds_set_client_mm_time_latency(RedsState *reds, RedClient *client, uint32_t latency)
2570 {
2571     // TODO: multi-client support for mm_time
2572     if (reds->mm_time_enabled) {
2573         // TODO: consider network latency
2574         if (latency > reds->mm_time_latency) {
2575             reds->mm_time_latency = latency;
2576             reds_send_mm_time(reds);
2577         } else {
2578             spice_debug("new latency %u is smaller than existing %u",
2579                         latency, reds->mm_time_latency);
2580         }
2581     } else {
2582         snd_set_playback_latency(client, latency);
2583     }
2584 }
2585 
reds_cleanup_net(SpiceServer * reds)2586 static void reds_cleanup_net(SpiceServer *reds)
2587 {
2588     if (reds->listen_socket != -1) {
2589        red_watch_remove(reds->listen_watch);
2590        if (reds->config->spice_listen_socket_fd != reds->listen_socket) {
2591           socket_close(reds->listen_socket);
2592        }
2593        reds->listen_watch = nullptr;
2594        reds->listen_socket = -1;
2595     }
2596     if (reds->secure_listen_socket != -1) {
2597        red_watch_remove(reds->secure_listen_watch);
2598        socket_close(reds->secure_listen_socket);
2599        reds->secure_listen_watch = nullptr;
2600        reds->secure_listen_socket = -1;
2601     }
2602 }
2603 
reds_init_net(RedsState * reds)2604 static int reds_init_net(RedsState *reds)
2605 {
2606     if (reds->config->spice_port != -1 || reds->config->spice_family == AF_UNIX) {
2607         reds->listen_socket = reds_init_socket(reds->config->spice_addr, reds->config->spice_port, reds->config->spice_family);
2608         if (-1 == reds->listen_socket) {
2609             return -1;
2610         }
2611         reds->listen_watch = reds_core_watch_add(reds, reds->listen_socket,
2612                                                  SPICE_WATCH_EVENT_READ,
2613                                                  reds_accept, reds);
2614         if (reds->listen_watch == nullptr) {
2615             return -1;
2616         }
2617     }
2618 
2619     if (reds->config->spice_secure_port != -1) {
2620         reds->secure_listen_socket = reds_init_socket(reds->config->spice_addr, reds->config->spice_secure_port,
2621                                                       reds->config->spice_family);
2622         if (-1 == reds->secure_listen_socket) {
2623             return -1;
2624         }
2625         reds->secure_listen_watch = reds_core_watch_add(reds, reds->secure_listen_socket,
2626                                                         SPICE_WATCH_EVENT_READ,
2627                                                         reds_accept_ssl_connection, reds);
2628         if (reds->secure_listen_watch == nullptr) {
2629             return -1;
2630         }
2631     }
2632 
2633     if (reds->config->spice_listen_socket_fd != -1 ) {
2634         reds->listen_socket = reds->config->spice_listen_socket_fd;
2635         reds->listen_watch = reds_core_watch_add(reds, reds->listen_socket,
2636                                                  SPICE_WATCH_EVENT_READ,
2637                                                  reds_accept, reds);
2638         if (reds->listen_watch == nullptr) {
2639             return -1;
2640         }
2641     }
2642     return 0;
2643 }
2644 
load_dh_params(SSL_CTX * ctx,char * file)2645 static int load_dh_params(SSL_CTX *ctx, char *file)
2646 {
2647     DH *ret = nullptr;
2648     BIO *bio;
2649 
2650     if ((bio = BIO_new_file(file, "r")) == nullptr) {
2651         spice_warning("Could not open DH file");
2652         red_dump_openssl_errors();
2653         return -1;
2654     }
2655 
2656     ret = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr);
2657     BIO_free(bio);
2658     if (ret == nullptr) {
2659         spice_warning("Could not read DH params");
2660         red_dump_openssl_errors();
2661         return -1;
2662     }
2663 
2664 
2665     if (SSL_CTX_set_tmp_dh(ctx, ret) < 0) {
2666         spice_warning("Could not set DH params");
2667         red_dump_openssl_errors();
2668         return -1;
2669     }
2670 
2671     return 0;
2672 }
2673 
2674 /*The password code is not thread safe*/
ssl_password_cb(char * buf,int size,int flags,void * userdata)2675 static int ssl_password_cb(char *buf, int size, int flags, void *userdata)
2676 {
2677     auto reds = (RedsState*) userdata;
2678     char *pass = reds->config->ssl_parameters.keyfile_password;
2679     int len = g_strlcpy(buf, pass, size);
2680     if (len >= size) {
2681         return 0;
2682     }
2683 
2684     return len;
2685 }
2686 
2687 #if OPENSSL_VERSION_NUMBER < 0x1010000FL
2688 static pthread_mutex_t *lock_cs;
2689 
pthreads_thread_id(CRYPTO_THREADID * tid)2690 static void pthreads_thread_id(CRYPTO_THREADID *tid)
2691 {
2692     CRYPTO_THREADID_set_numeric(tid, (unsigned long)pthread_self());
2693 }
2694 
pthreads_locking_callback(int mode,int type,const char * file,int line)2695 static void pthreads_locking_callback(int mode, int type, const char *file, int line)
2696 {
2697     if (mode & CRYPTO_LOCK) {
2698         pthread_mutex_lock(&(lock_cs[type]));
2699     } else {
2700         pthread_mutex_unlock(&(lock_cs[type]));
2701     }
2702 }
2703 
openssl_thread_setup(void)2704 static void openssl_thread_setup(void)
2705 {
2706     int i;
2707 
2708     /* Somebody else already setup threading for OpenSSL,
2709      * don't do it twice to avoid possible races.
2710      */
2711     if (CRYPTO_get_locking_callback() != NULL) {
2712         red_dump_openssl_errors();
2713         return;
2714     }
2715 
2716     lock_cs = (pthread_mutex_t*) OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
2717 
2718     for (i = 0; i < CRYPTO_num_locks(); i++) {
2719         pthread_mutex_init(&(lock_cs[i]), NULL);
2720     }
2721 
2722     CRYPTO_THREADID_set_callback(pthreads_thread_id);
2723     CRYPTO_set_locking_callback(pthreads_locking_callback);
2724 }
2725 
openssl_global_init_once(gpointer arg)2726 static gpointer openssl_global_init_once(gpointer arg)
2727 {
2728     SSL_library_init();
2729     SSL_load_error_strings();
2730 
2731     openssl_thread_setup();
2732 
2733     return NULL;
2734 }
2735 
openssl_global_init(void)2736 static inline void openssl_global_init(void)
2737 {
2738     static GOnce openssl_once = G_ONCE_INIT;
2739     g_once(&openssl_once, openssl_global_init_once, NULL);
2740 }
2741 
2742 #else
openssl_global_init()2743 static inline void openssl_global_init()
2744 {
2745 }
2746 #endif
2747 
reds_init_ssl(RedsState * reds)2748 static int reds_init_ssl(RedsState *reds)
2749 {
2750     const SSL_METHOD *ssl_method;
2751     int return_code;
2752     /* Limit connection to TLSv1.1 or newer.
2753      * When some other SSL/TLS version becomes obsolete, add it to this
2754      * variable. */
2755     long ssl_options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION | SSL_OP_NO_TLSv1;
2756 #ifdef SSL_OP_NO_RENEGOTIATION
2757     // With OpenSSL 1.1: Disable all renegotiation in TLSv1.2 and earlier
2758     ssl_options |= SSL_OP_NO_RENEGOTIATION;
2759 #endif
2760 
2761     /* Global system initialization*/
2762     openssl_global_init();
2763 
2764     /* Create our context*/
2765     /* SSLv23_method() handles TLSv1.x in addition to SSLv2/v3 */
2766     ssl_method = SSLv23_method();
2767     reds->ctx = SSL_CTX_new(ssl_method);
2768     if (!reds->ctx) {
2769         spice_warning("Could not allocate new SSL context");
2770         red_dump_openssl_errors();
2771         return -1;
2772     }
2773 
2774     SSL_CTX_set_options(reds->ctx, ssl_options);
2775 #if HAVE_DECL_SSL_CTX_SET_ECDH_AUTO || defined(SSL_CTX_set_ecdh_auto)
2776     SSL_CTX_set_ecdh_auto(reds->ctx, 1);
2777 #endif
2778 
2779     /* Load our keys and certificates*/
2780     return_code = SSL_CTX_use_certificate_chain_file(reds->ctx, reds->config->ssl_parameters.certs_file);
2781     if (return_code == 1) {
2782         spice_debug("Loaded certificates from %s", reds->config->ssl_parameters.certs_file);
2783     } else {
2784         spice_warning("Could not load certificates from %s", reds->config->ssl_parameters.certs_file);
2785         red_dump_openssl_errors();
2786         return -1;
2787     }
2788 
2789     SSL_CTX_set_default_passwd_cb(reds->ctx, ssl_password_cb);
2790     SSL_CTX_set_default_passwd_cb_userdata(reds->ctx, reds);
2791 
2792     return_code = SSL_CTX_use_PrivateKey_file(reds->ctx, reds->config->ssl_parameters.private_key_file,
2793                                               SSL_FILETYPE_PEM);
2794     if (return_code == 1) {
2795         spice_debug("Using private key from %s", reds->config->ssl_parameters.private_key_file);
2796     } else {
2797         spice_warning("Could not use private key file");
2798         return -1;
2799     }
2800 
2801     /* Load the CAs we trust*/
2802     return_code = SSL_CTX_load_verify_locations(reds->ctx, reds->config->ssl_parameters.ca_certificate_file, nullptr);
2803     if (return_code == 1) {
2804         spice_debug("Loaded CA certificates from %s", reds->config->ssl_parameters.ca_certificate_file);
2805     } else {
2806         spice_warning("Could not use CA file %s", reds->config->ssl_parameters.ca_certificate_file);
2807         red_dump_openssl_errors();
2808         return -1;
2809     }
2810 
2811     if (strlen(reds->config->ssl_parameters.dh_key_file) > 0) {
2812         if (load_dh_params(reds->ctx, reds->config->ssl_parameters.dh_key_file) < 0) {
2813             return -1;
2814         }
2815     }
2816 
2817     SSL_CTX_set_session_id_context(reds->ctx, (const unsigned char *)"SPICE", 5);
2818     if (strlen(reds->config->ssl_parameters.ciphersuite) > 0) {
2819         if (!SSL_CTX_set_cipher_list(reds->ctx, reds->config->ssl_parameters.ciphersuite)) {
2820             return -1;
2821         }
2822     }
2823 
2824     return 0;
2825 }
2826 
reds_cleanup(RedsState * reds)2827 static void reds_cleanup(RedsState *reds)
2828 {
2829 #ifdef RED_STATISTICS
2830     stat_file_unlink(reds->stat_file);
2831 #endif
2832 }
2833 
SPICE_DESTRUCTOR_FUNC(reds_exit)2834 SPICE_DESTRUCTOR_FUNC(reds_exit)
2835 {
2836     GList *l;
2837 
2838     pthread_mutex_lock(&global_reds_lock);
2839     for (l = servers; l != nullptr; l = l->next) {
2840         auto reds = (RedsState*) l->data;
2841         reds_cleanup(reds);
2842     }
2843     pthread_mutex_unlock(&global_reds_lock);
2844 }
2845 
on_activating_ticketing(RedsState * reds)2846 static inline void on_activating_ticketing(RedsState *reds)
2847 {
2848     if (!reds->config->ticketing_enabled && reds_main_channel_connected(reds)) {
2849         spice_warning("disconnecting");
2850         reds_disconnect(reds);
2851     }
2852 }
2853 
reds_config_set_image_compression(RedsState * reds,SpiceImageCompression image_compression)2854 static void reds_config_set_image_compression(RedsState *reds, SpiceImageCompression image_compression)
2855 {
2856     if (image_compression == reds->config->image_compression) {
2857         return;
2858     }
2859     switch (image_compression) {
2860     case SPICE_IMAGE_COMPRESSION_AUTO_LZ:
2861         spice_debug("ic auto_lz");
2862         break;
2863     case SPICE_IMAGE_COMPRESSION_AUTO_GLZ:
2864         spice_debug("ic auto_glz");
2865         break;
2866     case SPICE_IMAGE_COMPRESSION_QUIC:
2867         spice_debug("ic quic");
2868         break;
2869 #ifdef USE_LZ4
2870     case SPICE_IMAGE_COMPRESSION_LZ4:
2871         spice_debug("ic lz4");
2872         break;
2873 #endif
2874     case SPICE_IMAGE_COMPRESSION_LZ:
2875         spice_debug("ic lz");
2876         break;
2877     case SPICE_IMAGE_COMPRESSION_GLZ:
2878         spice_debug("ic glz");
2879         break;
2880     case SPICE_IMAGE_COMPRESSION_OFF:
2881         spice_debug("ic off");
2882         break;
2883     default:
2884         spice_warning("ic invalid");
2885         return;
2886     }
2887     reds->config->image_compression = image_compression;
2888     reds_on_ic_change(reds);
2889 }
2890 
reds_set_one_channel_security(RedsState * reds,int id,uint32_t security)2891 static void reds_set_one_channel_security(RedsState *reds, int id, uint32_t security)
2892 {
2893     ChannelSecurityOptions *security_options;
2894 
2895     if ((security_options = reds_find_channel_security(reds, id))) {
2896         security_options->options = security;
2897         return;
2898     }
2899     security_options = g_new(ChannelSecurityOptions, 1);
2900     security_options->channel_id = id;
2901     security_options->options = security;
2902     security_options->next = reds->config->channels_security;
2903     reds->config->channels_security = security_options;
2904 }
2905 
reds_mig_release(RedServerConfig * config)2906 static void reds_mig_release(RedServerConfig *config)
2907 {
2908     if (config->mig_spice) {
2909         g_free(config->mig_spice->cert_subject);
2910         g_free(config->mig_spice->host);
2911         g_free(config->mig_spice);
2912         config->mig_spice = nullptr;
2913     }
2914 }
2915 
reds_mig_started(RedsState * reds)2916 static void reds_mig_started(RedsState *reds)
2917 {
2918     spice_debug("trace");
2919     spice_assert(reds->config->mig_spice);
2920 
2921     reds->mig_inprogress = TRUE;
2922     reds->mig_wait_connect = TRUE;
2923     red_timer_start(reds->mig_timer, MIGRATE_TIMEOUT);
2924 }
2925 
reds_mig_fill_wait_disconnect(RedsState * reds)2926 static void reds_mig_fill_wait_disconnect(RedsState *reds)
2927 {
2928     spice_assert(!reds->clients.empty());
2929     /* tracking the clients, in order to ignore disconnection
2930      * of clients that got connected to the src after migration completion.*/
2931     for (auto client: reds->clients) {
2932         reds->mig_wait_disconnect_clients.push_front(client);
2933     }
2934     reds->mig_wait_connect = FALSE;
2935     reds->mig_wait_disconnect = TRUE;
2936     red_timer_start(reds->mig_timer, MIGRATE_TIMEOUT);
2937 }
2938 
reds_mig_cleanup_wait_disconnect(RedsState * reds)2939 static void reds_mig_cleanup_wait_disconnect(RedsState *reds)
2940 {
2941     reds->mig_wait_disconnect_clients.clear();
2942     reds->mig_wait_disconnect = FALSE;
2943 }
2944 
reds_mig_remove_wait_disconnect_client(RedsState * reds,RedClient * client)2945 static void reds_mig_remove_wait_disconnect_client(RedsState *reds, RedClient *client)
2946 {
2947     auto &clients(reds->mig_wait_disconnect_clients);
2948     g_warn_if_fail(std::find(clients.begin(), clients.end(), client) != clients.end());
2949 
2950     clients.remove(client);
2951     if (clients.empty()) {
2952        reds_mig_cleanup(reds);
2953     }
2954 }
2955 
reds_migrate_channels_seamless(RedsState * reds)2956 static void reds_migrate_channels_seamless(RedsState *reds)
2957 {
2958     RedClient *client;
2959 
2960     /* seamless migration is supported for only one client for now */
2961     client = reds_get_client(reds);
2962     client->migrate();
2963 }
2964 
reds_mig_finished(RedsState * reds,int completed)2965 static void reds_mig_finished(RedsState *reds, int completed)
2966 {
2967     spice_debug("trace");
2968 
2969     reds->mig_inprogress = TRUE;
2970 
2971     if (reds->src_do_seamless_migrate && completed) {
2972         reds_migrate_channels_seamless(reds);
2973     } else {
2974         reds->main_channel->migrate_src_complete(completed);
2975     }
2976 
2977     if (completed) {
2978         reds_mig_fill_wait_disconnect(reds);
2979     } else {
2980         reds_mig_cleanup(reds);
2981     }
2982     reds_mig_release(reds->config);
2983 }
2984 
migrate_timeout(RedsState * reds)2985 static void migrate_timeout(RedsState *reds)
2986 {
2987     spice_debug("trace");
2988     spice_assert(reds->mig_wait_connect || reds->mig_wait_disconnect);
2989     if (reds->mig_wait_connect) {
2990         /* we will fall back to the switch host scheme when migration completes */
2991         reds->main_channel->migrate_cancel_wait();
2992         /* in case part of the client haven't yet completed the previous migration, disconnect them */
2993         reds_mig_target_client_disconnect_all(reds);
2994         reds_mig_cleanup(reds);
2995     } else {
2996         reds_mig_disconnect(reds);
2997     }
2998 }
2999 
reds_get_mm_time(void)3000 uint32_t reds_get_mm_time(void)
3001 {
3002     return spice_get_monotonic_time_ns() / NSEC_PER_MILLISEC;
3003 }
3004 
reds_enable_mm_time(RedsState * reds)3005 void reds_enable_mm_time(RedsState *reds)
3006 {
3007     reds->mm_time_enabled = TRUE;
3008     reds->mm_time_latency = MM_TIME_DELTA;
3009     reds_send_mm_time(reds);
3010 }
3011 
reds_disable_mm_time(RedsState * reds)3012 void reds_disable_mm_time(RedsState *reds)
3013 {
3014     reds->mm_time_enabled = FALSE;
3015 }
3016 
3017 static red::shared_ptr<RedCharDevice>
attach_to_red_agent(RedsState * reds,SpiceCharDeviceInstance * sin)3018 attach_to_red_agent(RedsState *reds, SpiceCharDeviceInstance *sin)
3019 {
3020     RedCharDeviceVDIPort *dev = reds->agent_dev.get();
3021     SpiceCharDeviceInterface *sif;
3022 
3023     dev->priv->agent_attached = true;
3024     dev->reset_dev_instance(sin);
3025 
3026     reds->vdagent = sin;
3027     reds_update_mouse_mode(reds);
3028 
3029     sif = spice_char_device_get_interface(sin);
3030     if (sif->state) {
3031         sif->state(sin, 1);
3032     }
3033 
3034     if (!reds_main_channel_connected(reds)) {
3035         return reds->agent_dev;
3036     }
3037 
3038     dev->priv->read_filter.discard_all = FALSE;
3039     dev->priv->plug_generation++;
3040 
3041     if (dev->priv->mig_data ||
3042         reds->main_channel->is_waiting_for_migrate_data()) {
3043         /* Migration in progress (code is running on the destination host):
3044          * 1.  Add the client to spice char device, if it was not already added.
3045          * 2.a If this (qemu-kvm state load side of migration) happens first
3046          *     then wait for spice migration data to arrive. Otherwise
3047          * 2.b If this happens second ==> we already have spice migrate data
3048          *     then restore state
3049          */
3050         auto client_opaque =
3051             (RedCharDeviceClientOpaque *) reds_get_client(reds);
3052         if (!dev->client_exists(client_opaque)) {
3053             int client_added;
3054 
3055             client_added = dev->client_add(client_opaque, TRUE,
3056                                            REDS_VDI_PORT_NUM_RECEIVE_BUFFS,
3057                                            REDS_AGENT_WINDOW_SIZE, ~0, TRUE);
3058 
3059             if (!client_added) {
3060                 spice_warning("failed to add client to agent");
3061                 reds_disconnect(reds);
3062             }
3063         }
3064 
3065         if (dev->priv->mig_data) {
3066             spice_debug("restoring dev from stored migration data");
3067             spice_assert(dev->priv->plug_generation == 1);
3068             reds_agent_state_restore(reds, dev->priv->mig_data);
3069             g_free(dev->priv->mig_data);
3070             dev->priv->mig_data = nullptr;
3071         }
3072         else {
3073             spice_debug("waiting for migration data");
3074         }
3075     } else {
3076         /* we will associate the client with the char device, upon reds_on_main_agent_start,
3077          * in response to MSGC_AGENT_START */
3078         reds->main_channel->push_agent_connected();
3079     }
3080 
3081     return reds->agent_dev;
3082 }
3083 
spice_server_char_device_wakeup(SpiceCharDeviceInstance * sin)3084 SPICE_GNUC_VISIBLE void spice_server_char_device_wakeup(SpiceCharDeviceInstance* sin)
3085 {
3086     if (!sin->st) {
3087         spice_warning("no RedCharDevice attached to instance %p", sin);
3088         return;
3089     }
3090     sin->st->wakeup();
3091 }
3092 
3093 #define SUBTYPE_VDAGENT "vdagent"
3094 #define SUBTYPE_SMARTCARD "smartcard"
3095 #define SUBTYPE_USBREDIR "usbredir"
3096 #define SUBTYPE_PORT "port"
3097 
3098 static const char *const spice_server_char_device_recognized_subtypes_list[] = {
3099     SUBTYPE_VDAGENT,
3100 #ifdef USE_SMARTCARD
3101     SUBTYPE_SMARTCARD,
3102 #endif
3103     SUBTYPE_USBREDIR,
3104     nullptr,
3105 };
3106 
spice_server_char_device_recognized_subtypes(void)3107 SPICE_GNUC_VISIBLE const char** spice_server_char_device_recognized_subtypes(void)
3108 {
3109     return (const char **) spice_server_char_device_recognized_subtypes_list;
3110 }
3111 
reds_add_char_device(RedsState * reds,const red::shared_ptr<RedCharDevice> & dev)3112 static void reds_add_char_device(RedsState *reds, const red::shared_ptr<RedCharDevice> &dev)
3113 {
3114     reds->char_devices.push_front(dev);
3115 }
3116 
reds_remove_char_device(RedsState * reds,RedCharDevice * dev)3117 static void reds_remove_char_device(RedsState *reds, RedCharDevice *dev)
3118 {
3119     g_return_if_fail(reds != nullptr);
3120     auto &devs(reds->char_devices);
3121     g_warn_if_fail(std::find(devs.begin(), devs.end(),
3122                              red::shared_ptr<RedCharDevice>(dev)) != devs.end());
3123 
3124     devs.remove(red::shared_ptr<RedCharDevice>(dev));
3125 }
3126 
3127 static int
spice_server_char_device_add_interface(SpiceServer * reds,SpiceBaseInstance * sin)3128 spice_server_char_device_add_interface(SpiceServer *reds, SpiceBaseInstance *sin)
3129 {
3130     SpiceCharDeviceInstance* char_device =
3131             SPICE_UPCAST(SpiceCharDeviceInstance, sin);
3132     red::shared_ptr<RedCharDevice> dev_state;
3133 
3134     spice_debug("CHAR_DEVICE %s", char_device->subtype);
3135     if (strcmp(char_device->subtype, SUBTYPE_VDAGENT) == 0) {
3136         if (reds->vdagent) {
3137             spice_warning("vdagent already attached");
3138             return -1;
3139         }
3140         dev_state = attach_to_red_agent(reds, char_device);
3141     }
3142 #ifdef USE_SMARTCARD
3143     else if (strcmp(char_device->subtype, SUBTYPE_SMARTCARD) == 0) {
3144         dev_state = smartcard_device_connect(reds, char_device);
3145         if (!dev_state) {
3146             return -1;
3147         }
3148     }
3149 #endif
3150     else if (strcmp(char_device->subtype, SUBTYPE_USBREDIR) == 0) {
3151         dev_state = spicevmc_device_connect(reds, char_device, SPICE_CHANNEL_USBREDIR);
3152     }
3153     else if (strcmp(char_device->subtype, SUBTYPE_PORT) == 0) {
3154         if (strcmp(char_device->portname, "org.spice-space.webdav.0") == 0) {
3155             dev_state = spicevmc_device_connect(reds, char_device, SPICE_CHANNEL_WEBDAV);
3156         } else if (strcmp(char_device->portname, "org.spice-space.stream.0") == 0) {
3157             dev_state = stream_device_connect(reds, char_device);
3158         } else {
3159             dev_state = spicevmc_device_connect(reds, char_device, SPICE_CHANNEL_PORT);
3160         }
3161     }
3162 
3163     if (dev_state) {
3164         /* When spicevmc_device_connect() is called to create a RedCharDevice,
3165          * it also assigns that as the internal state for char_device. This is
3166          * just a sanity check to ensure that assumption is correct */
3167         spice_assert(dev_state.get() == char_device->st);
3168 
3169         /* setting the char_device state to "started" for backward compatibily with
3170          * qemu releases that don't call spice api for start/stop (not implemented yet) */
3171         if (reds->vm_running) {
3172             dev_state->start();
3173         }
3174         reds_add_char_device(reds, dev_state);
3175     } else {
3176         spice_warning("failed to create device state for %s", char_device->subtype);
3177         return -1;
3178     }
3179     return 0;
3180 }
3181 
spice_server_char_device_remove_interface(RedsState * reds,SpiceBaseInstance * sin)3182 static int spice_server_char_device_remove_interface(RedsState *reds, SpiceBaseInstance *sin)
3183 {
3184     SpiceCharDeviceInstance* char_device =
3185             SPICE_UPCAST(SpiceCharDeviceInstance, sin);
3186 
3187     spice_debug("remove CHAR_DEVICE %s", char_device->subtype);
3188     if (strcmp(char_device->subtype, SUBTYPE_VDAGENT) == 0) {
3189         g_return_val_if_fail(char_device == reds->vdagent, -1);
3190         if (reds->vdagent) {
3191             reds_agent_remove(reds);
3192             reds->agent_dev->reset_dev_instance(nullptr);
3193         }
3194     }
3195 
3196     if (char_device->st) {
3197         auto st = char_device->st;
3198         char_device->st = nullptr;
3199         reds_remove_char_device(reds, st);
3200     }
3201     return 0;
3202 }
3203 
spice_server_add_interface(SpiceServer * reds,SpiceBaseInstance * sin)3204 SPICE_GNUC_VISIBLE int spice_server_add_interface(SpiceServer *reds,
3205                                                   SpiceBaseInstance *sin)
3206 {
3207     const SpiceBaseInterface *base_interface = sin->sif;
3208 
3209     if (strcmp(base_interface->type, SPICE_INTERFACE_KEYBOARD) == 0) {
3210         spice_debug("SPICE_INTERFACE_KEYBOARD");
3211         if (base_interface->major_version != SPICE_INTERFACE_KEYBOARD_MAJOR ||
3212             base_interface->minor_version > SPICE_INTERFACE_KEYBOARD_MINOR) {
3213             spice_warning("unsupported keyboard interface");
3214             return -1;
3215         }
3216         if (reds->inputs_channel->set_keyboard(SPICE_UPCAST(SpiceKbdInstance, sin)) != 0) {
3217             return -1;
3218         }
3219     } else if (strcmp(base_interface->type, SPICE_INTERFACE_MOUSE) == 0) {
3220         spice_debug("SPICE_INTERFACE_MOUSE");
3221         if (base_interface->major_version != SPICE_INTERFACE_MOUSE_MAJOR ||
3222             base_interface->minor_version > SPICE_INTERFACE_MOUSE_MINOR) {
3223             spice_warning("unsupported mouse interface");
3224             return -1;
3225         }
3226         if (reds->inputs_channel->set_mouse(SPICE_UPCAST(SpiceMouseInstance, sin)) != 0) {
3227             return -1;
3228         }
3229     } else if (strcmp(base_interface->type, SPICE_INTERFACE_QXL) == 0) {
3230         QXLInstance *qxl;
3231 
3232         spice_debug("SPICE_INTERFACE_QXL");
3233         if (base_interface->major_version != SPICE_INTERFACE_QXL_MAJOR ||
3234             base_interface->minor_version > SPICE_INTERFACE_QXL_MINOR) {
3235             spice_warning("unsupported qxl interface");
3236             return -1;
3237         }
3238 
3239         qxl = SPICE_UPCAST(QXLInstance, sin);
3240         if (qxl->id < 0) {
3241             spice_warning("invalid QXL ID");
3242             return -1;
3243         }
3244         if (reds_find_channel(reds, SPICE_CHANNEL_DISPLAY, qxl->id)) {
3245             spice_warning("QXL ID already allocated");
3246             return -1;
3247         }
3248         red_qxl_init(reds, qxl);
3249         reds->qxl_instances.push_front(qxl);
3250 
3251         /* this function has to be called after the qxl is on the list
3252          * as QXLInstance clients expect the qxl to be on the list when
3253          * this callback is called. This as clients assume they can start the
3254          * qxl_instances. Also note that this should be the first callback to
3255          * be called. */
3256         red_qxl_attach_worker(qxl);
3257         red_qxl_set_compression_level(qxl, calc_compression_level(reds));
3258         if (reds->vm_running) {
3259             red_qxl_start(qxl);
3260         }
3261     } else if (strcmp(base_interface->type, SPICE_INTERFACE_TABLET) == 0) {
3262         SpiceTabletInstance *tablet = SPICE_UPCAST(SpiceTabletInstance, sin);
3263         spice_debug("SPICE_INTERFACE_TABLET");
3264         if (base_interface->major_version != SPICE_INTERFACE_TABLET_MAJOR ||
3265             base_interface->minor_version > SPICE_INTERFACE_TABLET_MINOR) {
3266             spice_warning("unsupported tablet interface");
3267             return -1;
3268         }
3269         if (reds->inputs_channel->set_tablet(tablet) != 0) {
3270             return -1;
3271         }
3272         reds_update_mouse_mode(reds);
3273         if (reds->is_client_mouse_allowed) {
3274             reds->inputs_channel->set_tablet_logical_size(reds->monitor_mode.x_res, reds->monitor_mode.y_res);
3275         }
3276 
3277     } else if (strcmp(base_interface->type, SPICE_INTERFACE_PLAYBACK) == 0) {
3278         spice_debug("SPICE_INTERFACE_PLAYBACK");
3279         if (base_interface->major_version != SPICE_INTERFACE_PLAYBACK_MAJOR ||
3280             base_interface->minor_version > SPICE_INTERFACE_PLAYBACK_MINOR) {
3281             spice_warning("unsupported playback interface");
3282             return -1;
3283         }
3284         snd_attach_playback(reds, SPICE_UPCAST(SpicePlaybackInstance, sin));
3285 
3286     } else if (strcmp(base_interface->type, SPICE_INTERFACE_RECORD) == 0) {
3287         spice_debug("SPICE_INTERFACE_RECORD");
3288         if (base_interface->major_version != SPICE_INTERFACE_RECORD_MAJOR ||
3289             base_interface->minor_version > SPICE_INTERFACE_RECORD_MINOR) {
3290             spice_warning("unsupported record interface");
3291             return -1;
3292         }
3293         snd_attach_record(reds, SPICE_UPCAST(SpiceRecordInstance, sin));
3294 
3295     } else if (strcmp(base_interface->type, SPICE_INTERFACE_CHAR_DEVICE) == 0) {
3296         if (base_interface->major_version != SPICE_INTERFACE_CHAR_DEVICE_MAJOR ||
3297             base_interface->minor_version > SPICE_INTERFACE_CHAR_DEVICE_MINOR) {
3298             spice_warning("unsupported char device interface");
3299             return -1;
3300         }
3301         spice_server_char_device_add_interface(reds, sin);
3302 
3303     } else if (strcmp(base_interface->type, SPICE_INTERFACE_MIGRATION) == 0) {
3304         spice_debug("SPICE_INTERFACE_MIGRATION");
3305         if (reds->migration_interface) {
3306             spice_warning("already have migration");
3307             return -1;
3308         }
3309 
3310         if (base_interface->major_version != SPICE_INTERFACE_MIGRATION_MAJOR ||
3311             base_interface->minor_version > SPICE_INTERFACE_MIGRATION_MINOR) {
3312             spice_warning("unsupported migration interface");
3313             return -1;
3314         }
3315         reds->migration_interface = SPICE_UPCAST(SpiceMigrateInstance, sin);
3316         reds->migration_interface->st = (SpiceMigrateState *)(intptr_t)1; // dummy pointer
3317     }
3318 
3319     return 0;
3320 }
3321 
spice_server_remove_interface(SpiceBaseInstance * sin)3322 SPICE_GNUC_VISIBLE int spice_server_remove_interface(SpiceBaseInstance *sin)
3323 {
3324     RedsState *reds;
3325     const SpiceBaseInterface *base_interface;
3326 
3327     g_return_val_if_fail(sin != nullptr, -1);
3328 
3329     base_interface = sin->sif;
3330     if (strcmp(base_interface->type, SPICE_INTERFACE_TABLET) == 0) {
3331         SpiceTabletInstance *tablet = SPICE_UPCAST(SpiceTabletInstance, sin);
3332         g_return_val_if_fail(tablet->st != nullptr, -1);
3333         reds = spice_tablet_state_get_server(tablet->st);
3334         spice_debug("remove SPICE_INTERFACE_TABLET");
3335         reds->inputs_channel->detach_tablet(tablet);
3336         reds_update_mouse_mode(reds);
3337     } else if (strcmp(base_interface->type, SPICE_INTERFACE_PLAYBACK) == 0) {
3338         spice_debug("remove SPICE_INTERFACE_PLAYBACK");
3339         snd_detach_playback(SPICE_UPCAST(SpicePlaybackInstance, sin));
3340     } else if (strcmp(base_interface->type, SPICE_INTERFACE_RECORD) == 0) {
3341         spice_debug("remove SPICE_INTERFACE_RECORD");
3342         snd_detach_record(SPICE_UPCAST(SpiceRecordInstance, sin));
3343     } else if (strcmp(base_interface->type, SPICE_INTERFACE_CHAR_DEVICE) == 0) {
3344         SpiceCharDeviceInstance *char_device = SPICE_UPCAST(SpiceCharDeviceInstance, sin);
3345         g_return_val_if_fail(char_device->st != nullptr, -1);
3346         reds = char_device->st->get_server();
3347         return spice_server_char_device_remove_interface(reds, sin);
3348     } else if (strcmp(base_interface->type, SPICE_INTERFACE_QXL) == 0) {
3349         QXLInstance *qxl;
3350 
3351         qxl = SPICE_UPCAST(QXLInstance, sin);
3352         g_return_val_if_fail(qxl->st != nullptr, -1);
3353         reds = red_qxl_get_server(qxl->st);
3354         reds->qxl_instances.remove(qxl); // XXX owning
3355         red_qxl_destroy(qxl);
3356     } else {
3357         spice_warning("VD_INTERFACE_REMOVING unsupported");
3358         return -1;
3359     }
3360 
3361     return 0;
3362 }
3363 
do_spice_init(RedsState * reds,SpiceCoreInterface * core_interface)3364 static int do_spice_init(RedsState *reds, SpiceCoreInterface *core_interface)
3365 {
3366     spice_debug("starting %s", VERSION);
3367 
3368     if (core_interface->base.major_version != SPICE_INTERFACE_CORE_MAJOR) {
3369         spice_warning("bad core interface version");
3370         goto err;
3371     }
3372     reds->core = core_interface_adapter;
3373     reds->core.public_interface = core_interface;
3374     reds->agent_dev = red::make_shared<RedCharDeviceVDIPort>(reds);
3375     reds_update_agent_properties(reds);
3376     reds->main_dispatcher = red::make_shared<MainDispatcher>(reds);
3377     reds->mig_target_clients = nullptr;
3378     reds->vm_running = TRUE; /* for backward compatibility */
3379 
3380     if (!(reds->mig_timer = reds->core.timer_new(migrate_timeout, reds))) {
3381         spice_error("migration timer create failed");
3382     }
3383     /* Note that this will not actually send the mm_time to the client because
3384      * the main channel is not connected yet. This would have been redundant
3385      * with the RED_PIPE_ITEM_TYPE_MAIN_INIT message anyway.
3386      */
3387     reds_enable_mm_time(reds);
3388 
3389     if (reds_init_net(reds) < 0) {
3390         spice_warning("Failed to open SPICE sockets");
3391         goto err;
3392     }
3393     if (reds->secure_listen_socket != -1) {
3394         if (reds_init_ssl(reds) < 0) {
3395             goto err;
3396         }
3397     }
3398 #if HAVE_SASL
3399     int saslerr;
3400     if ((saslerr = sasl_server_init(NULL, reds->config->sasl_appname ?
3401                                     reds->config->sasl_appname : "spice")) != SASL_OK) {
3402         spice_error("Failed to initialize SASL auth %s",
3403                   sasl_errstring(saslerr, NULL, NULL));
3404         goto err;
3405     }
3406 #endif
3407 
3408     reds->main_channel = main_channel_new(reds);
3409     reds->inputs_channel = inputs_channel_new(reds);
3410 
3411     reds->mouse_mode = SPICE_MOUSE_MODE_SERVER;
3412 
3413     spice_buffer_free(&reds->client_monitors_config);
3414 
3415     reds->allow_multiple_clients = getenv(SPICE_DEBUG_ALLOW_MC_ENV) != nullptr;
3416     if (reds->allow_multiple_clients) {
3417         spice_warning("spice: allowing multiple client connections");
3418     }
3419     pthread_mutex_lock(&global_reds_lock);
3420     servers = g_list_prepend(servers, reds);
3421     pthread_mutex_unlock(&global_reds_lock);
3422     return 0;
3423 
3424 err:
3425     reds_cleanup_net(reds);
3426     return -1;
3427 }
3428 
3429 static const char default_renderer[] = "sw";
3430 #if defined(HAVE_GSTREAMER_1_0) || defined(HAVE_GSTREAMER_0_10)
3431 #define GSTREAMER_CODECS "gstreamer:mjpeg;gstreamer:h264;gstreamer:vp8;gstreamer:vp9;"
3432 #else
3433 #define GSTREAMER_CODECS ""
3434 #endif
3435 static const char default_video_codecs[] = "spice:mjpeg;" GSTREAMER_CODECS;
3436 
3437 /* new interface */
spice_server_new(void)3438 SPICE_GNUC_VISIBLE SpiceServer *spice_server_new(void)
3439 {
3440     const char *record_filename;
3441     auto reds = new RedsState;
3442 
3443     reds->config = g_new0(RedServerConfig, 1);
3444     reds->config->default_channel_security =
3445         SPICE_CHANNEL_SECURITY_NONE | SPICE_CHANNEL_SECURITY_SSL;
3446     reds->config->renderers = g_array_sized_new(FALSE, TRUE, sizeof(uint32_t), RED_RENDERER_LAST);
3447     reds->config->spice_port = -1;
3448     reds->config->spice_secure_port = -1;
3449     reds->config->spice_listen_socket_fd = -1;
3450     reds->config->spice_family = PF_UNSPEC;
3451     reds->config->sasl_enabled = 0; // sasl disabled by default
3452 #if HAVE_SASL
3453     reds->config->sasl_appname = NULL; // default to "spice" if NULL
3454 #endif
3455     reds->config->spice_uuid_is_set = FALSE;
3456     memset(reds->config->spice_uuid, 0, sizeof(reds->config->spice_uuid));
3457     reds->config->ticketing_enabled = TRUE; /* ticketing enabled by default */
3458     reds->config->streaming_video = SPICE_STREAM_VIDEO_FILTER;
3459     reds->config->video_codecs = g_array_new(FALSE, FALSE, sizeof(RedVideoCodec));
3460     reds->config->image_compression = SPICE_IMAGE_COMPRESSION_AUTO_GLZ;
3461     reds->config->playback_compression = TRUE;
3462     reds->config->jpeg_state = SPICE_WAN_COMPRESSION_AUTO;
3463     reds->config->zlib_glz_state = SPICE_WAN_COMPRESSION_AUTO;
3464     reds->config->agent_mouse = TRUE;
3465     reds->config->agent_copypaste = TRUE;
3466     reds->config->agent_file_xfer = TRUE;
3467     reds->config->exit_on_disconnect = FALSE;
3468 #ifdef RED_STATISTICS
3469     reds->stat_file = stat_file_new(REDS_MAX_STAT_NODES);
3470     /* Create an initial node. This will be the 0 node making easier
3471      * to initialize node references.
3472      */
3473     stat_file_add_node(reds->stat_file, INVALID_STAT_REF, "default_channel", TRUE);
3474 #endif
3475     reds->listen_socket = -1;
3476     reds->secure_listen_socket = -1;
3477 
3478     /* This environment was in red-worker so the "WORKER" in it.
3479      * For compatibility reason we maintain the old name */
3480     record_filename = getenv("SPICE_WORKER_RECORD_FILENAME");
3481     if (record_filename) {
3482         reds->record = red_record_new(record_filename);
3483     }
3484     return reds;
3485 }
3486 
3487 struct EnumNames {
3488     uint32_t id;
3489     const char *name;
3490 };
3491 
get_name_index(const EnumNames names[],const char * name,uint32_t * index)3492 static gboolean get_name_index(const EnumNames names[], const char *name, uint32_t *index)
3493 {
3494     if (name) {
3495         int i;
3496         for (i = 0; names[i].name; i++) {
3497             if (strcmp(name, names[i].name) == 0) {
3498                 *index = i;
3499                 return TRUE;
3500             }
3501         }
3502     }
3503     return FALSE;
3504 }
3505 
3506 /* returns NULL if index is invalid. */
get_index_name(const EnumNames names[],uint32_t index)3507 static const char *get_index_name(const EnumNames names[], uint32_t index)
3508 {
3509     while (names->name != nullptr && names->id != index) {
3510         names++;
3511     }
3512 
3513     return names->name;
3514 }
3515 
3516 static const EnumNames renderer_names[] = {
3517     {RED_RENDERER_SW, "sw"},
3518     {RED_RENDERER_INVALID, nullptr},
3519 };
3520 
reds_add_renderer(RedsState * reds,const char * name)3521 static gboolean reds_add_renderer(RedsState *reds, const char *name)
3522 {
3523     uint32_t index;
3524 
3525     if (reds->config->renderers->len == RED_RENDERER_LAST ||
3526         !get_name_index(renderer_names, name, &index)) {
3527         return FALSE;
3528     }
3529     g_array_append_val(reds->config->renderers, renderer_names[index].id);
3530     return TRUE;
3531 }
3532 
3533 static const EnumNames video_encoder_names[] = {
3534     {0, "spice"},
3535     {1, "gstreamer"},
3536     {0, nullptr},
3537 };
3538 
3539 static const new_video_encoder_t video_encoder_procs[] = {
3540     &mjpeg_encoder_new,
3541 #if defined(HAVE_GSTREAMER_1_0) || defined(HAVE_GSTREAMER_0_10)
3542     &gstreamer_encoder_new,
3543 #else
3544     nullptr,
3545 #endif
3546 };
3547 
3548 static const EnumNames video_codec_names[] = {
3549     {SPICE_VIDEO_CODEC_TYPE_MJPEG, "mjpeg"},
3550     {SPICE_VIDEO_CODEC_TYPE_VP8, "vp8"},
3551     {SPICE_VIDEO_CODEC_TYPE_H264, "h264"},
3552     {SPICE_VIDEO_CODEC_TYPE_VP9, "vp9"},
3553     {0, nullptr},
3554 };
3555 
3556 static const int video_codec_caps[] = {
3557     SPICE_DISPLAY_CAP_CODEC_MJPEG,
3558     SPICE_DISPLAY_CAP_CODEC_VP8,
3559     SPICE_DISPLAY_CAP_CODEC_H264,
3560     SPICE_DISPLAY_CAP_CODEC_VP9,
3561 };
3562 
reds_get_video_codec_fullname(RedVideoCodec * codec)3563 char *reds_get_video_codec_fullname(RedVideoCodec *codec)
3564 {
3565     int i;
3566     const char *encoder_name = nullptr;
3567     const char *codec_name = get_index_name(video_codec_names, codec->type);
3568 
3569     spice_assert(codec_name);
3570 
3571     for (i = 0; i < G_N_ELEMENTS(video_encoder_procs); i++) {
3572         if (video_encoder_procs[i] == codec->create) {
3573             encoder_name = get_index_name(video_encoder_names, i);
3574             break;
3575         }
3576     }
3577     spice_assert(encoder_name);
3578 
3579     return g_strdup_printf("%s:%s", encoder_name, codec_name);
3580 }
3581 
3582 /* Parses the given codec string and returns newly-allocated strings describing
3583  * the next encoder and codec in the list. These strings must be freed by the
3584  * caller.
3585  *
3586  * @codecs: a codec string in the following format: encoder:codec;encoder:codec
3587  * @encoder: a location to return the parsed encoder
3588  * @codec: a location to return the parsed codec
3589  * @return the position of the next codec in the string
3590  */
parse_next_video_codec(char * codecs,char ** encoder,char ** codec)3591 static char* parse_next_video_codec(char *codecs, char **encoder, char **codec)
3592 {
3593     if (!codecs) {
3594         return nullptr;
3595     }
3596     codecs += strspn(codecs, ";");
3597     if (!*codecs) {
3598         return nullptr;
3599     }
3600     int end_encoder, end_codec = -1;
3601     *encoder = *codec = nullptr;
3602     if (sscanf(codecs, "%*[0-9a-zA-Z_]:%n%*[0-9a-zA-Z_];%n", &end_encoder, &end_codec) == 0
3603         && end_codec > 0) {
3604         codecs[end_encoder - 1] = '\0';
3605         codecs[end_codec - 1] = '\0';
3606         *encoder = codecs;
3607         *codec = codecs + end_encoder;
3608         return codecs + end_codec;
3609     }
3610     return codecs + strcspn(codecs, ";");
3611 }
3612 
3613 /* Enable the encoders/codecs from the list specified in @codecs.
3614  *
3615  * @reds: the #RedsState to modify
3616  * @codecs: a codec string in the following format: encoder:codec;encoder:codec
3617  * @installed: (optional): a location to return the number of codecs successfull installed
3618  * @return -1 if @codecs is %NULL (@installed is not modified) or the number of invalid
3619  *         encoders/codecs found in @codecs.
3620  */
reds_set_video_codecs_from_string(RedsState * reds,const char * codecs,unsigned int * installed)3621 static int reds_set_video_codecs_from_string(RedsState *reds, const char *codecs,
3622                                              unsigned int *installed)
3623 {
3624     char *encoder_name, *codec_name;
3625     GArray *video_codecs;
3626     int invalid_codecs = 0;
3627 
3628     g_return_val_if_fail(codecs != nullptr, -1);
3629 
3630     if (strcmp(codecs, "auto") == 0) {
3631         codecs = default_video_codecs;
3632     }
3633 
3634     video_codecs = g_array_new(FALSE, FALSE, sizeof(RedVideoCodec));
3635     char *codecs_copy = g_strdup_printf("%s;", codecs);
3636     char *c = codecs_copy;
3637     while ( (c = parse_next_video_codec(c, &encoder_name, &codec_name)) ) {
3638         uint32_t encoder_index, codec_index;
3639         if (!encoder_name || !codec_name) {
3640             spice_warning("spice: invalid encoder:codec value at %s", codecs);
3641             invalid_codecs++;
3642 
3643         } else if (!get_name_index(video_encoder_names, encoder_name, &encoder_index)){
3644             spice_warning("spice: unknown video encoder %s", encoder_name);
3645             invalid_codecs++;
3646 
3647         } else if (!get_name_index(video_codec_names, codec_name, &codec_index)) {
3648             spice_warning("spice: unknown video codec %s", codec_name);
3649             invalid_codecs++;
3650 
3651         } else if (!video_encoder_procs[encoder_index]) {
3652             spice_warning("spice: unsupported video encoder %s", encoder_name);
3653             invalid_codecs++;
3654 
3655         } else {
3656             RedVideoCodec new_codec;
3657             new_codec.create = video_encoder_procs[encoder_index];
3658             new_codec.type = (SpiceVideoCodecType) video_codec_names[codec_index].id;
3659             new_codec.cap = video_codec_caps[codec_index];
3660             g_array_append_val(video_codecs, new_codec);
3661         }
3662 
3663         codecs = c;
3664     }
3665 
3666     if (installed) {
3667         *installed = video_codecs->len;
3668     }
3669 
3670     if (video_codecs->len == 0) {
3671         spice_warning("Failed to set video codecs, input string: '%s'", codecs);
3672         g_array_unref(video_codecs);
3673     } else {
3674         reds_set_video_codecs(reds, video_codecs);
3675     }
3676 
3677     g_free(codecs_copy);
3678 
3679     return invalid_codecs;
3680 }
3681 
spice_server_init(SpiceServer * reds,SpiceCoreInterface * core)3682 SPICE_GNUC_VISIBLE int spice_server_init(SpiceServer *reds, SpiceCoreInterface *core)
3683 {
3684     int ret;
3685 
3686     ret = do_spice_init(reds, core);
3687     if (reds->config->renderers->len == 0) {
3688         reds_add_renderer(reds, default_renderer);
3689     }
3690     if (reds->config->video_codecs->len == 0) {
3691         reds_set_video_codecs_from_string(reds, default_video_codecs, nullptr);
3692     }
3693     return ret;
3694 }
3695 
reds_config_free(RedServerConfig * config)3696 static void reds_config_free(RedServerConfig *config)
3697 {
3698     ChannelSecurityOptions *curr, *next;
3699 
3700     reds_mig_release(config);
3701     for (curr = config->channels_security; curr; curr = next) {
3702         next = curr->next;
3703         g_free(curr);
3704     }
3705 #if HAVE_SASL
3706     g_free(config->sasl_appname);
3707 #endif
3708     g_free(config->spice_name);
3709     g_array_unref(config->renderers);
3710     g_array_unref(config->video_codecs);
3711     g_free(config);
3712 }
3713 
spice_server_destroy(SpiceServer * reds)3714 SPICE_GNUC_VISIBLE void spice_server_destroy(SpiceServer *reds)
3715 {
3716     /* remove the server from the list of servers so that we don't attempt to
3717      * free it again at exit */
3718     pthread_mutex_lock(&global_reds_lock);
3719     servers = g_list_remove(servers, reds);
3720     pthread_mutex_unlock(&global_reds_lock);
3721 
3722     for (auto qxl: reds->qxl_instances) {
3723         red_qxl_destroy(qxl);
3724     }
3725 
3726     if (reds->inputs_channel) {
3727         reds->inputs_channel->destroy();
3728     }
3729     /* This requires a bit of explanation on how reference counting is
3730      * not enough. The full reply is in docs/spice_threading_model.txt,
3731      * mainly the RedChannels are owned by both RedsState and
3732      * RedChannelClient so we need both to get destroyed. This call
3733      * remove RedChannelClients */
3734     if (reds->main_channel) {
3735         reds->main_channel->destroy();
3736     }
3737     red_timer_remove(reds->mig_timer);
3738 
3739     if (reds->ctx) {
3740         SSL_CTX_free(reds->ctx);
3741     }
3742 
3743     reds->main_dispatcher.reset();
3744     reds_cleanup_net(reds);
3745     reds->agent_dev.reset();
3746 
3747     // NOTE: don't replace with g_list_free_full as this function that passed callback
3748     // don't change the list while unreferencing in this case will change it.
3749     reds->char_devices.clear();
3750 
3751     spice_buffer_free(&reds->client_monitors_config);
3752     red_record_unref(reds->record);
3753     reds_cleanup(reds);
3754 #ifdef RED_STATISTICS
3755     stat_file_free(reds->stat_file);
3756 #endif
3757 
3758     reds_config_free(reds->config);
3759     delete reds;
3760 }
3761 
spice_get_current_compat_version(void)3762 SPICE_GNUC_VISIBLE spice_compat_version_t spice_get_current_compat_version(void)
3763 {
3764     return SPICE_COMPAT_VERSION_CURRENT;
3765 }
3766 
spice_server_set_compat_version(SpiceServer * reds,spice_compat_version_t version)3767 SPICE_GNUC_VISIBLE int spice_server_set_compat_version(SpiceServer *reds,
3768                                                        spice_compat_version_t version)
3769 {
3770     if (version < SPICE_COMPAT_VERSION_0_6) {
3771         /* We don't support 0.4 compat mode atm */
3772         return -1;
3773     }
3774 
3775     if (version > SPICE_COMPAT_VERSION_CURRENT) {
3776         /* Not compatible with future versions */
3777         return -1;
3778     }
3779     return 0;
3780 }
3781 
spice_server_set_port(SpiceServer * reds,int port)3782 SPICE_GNUC_VISIBLE int spice_server_set_port(SpiceServer *reds, int port)
3783 {
3784     if (port < 0 || port > 0xffff) {
3785         return -1;
3786     }
3787     reds->config->spice_port = port;
3788     return 0;
3789 }
3790 
spice_server_set_addr(SpiceServer * reds,const char * addr,int flags)3791 SPICE_GNUC_VISIBLE void spice_server_set_addr(SpiceServer *reds, const char *addr, int flags)
3792 {
3793     g_strlcpy(reds->config->spice_addr, addr, sizeof(reds->config->spice_addr));
3794 
3795     if (flags == SPICE_ADDR_FLAG_IPV4_ONLY) {
3796         reds->config->spice_family = PF_INET;
3797     } else if (flags == SPICE_ADDR_FLAG_IPV6_ONLY) {
3798         reds->config->spice_family = PF_INET6;
3799     } else if (flags == SPICE_ADDR_FLAG_UNIX_ONLY) {
3800         reds->config->spice_family = AF_UNIX;
3801     } else if (flags != 0) {
3802         spice_warning("unknown address flag: 0x%X", flags);
3803     }
3804 }
3805 
spice_server_set_listen_socket_fd(SpiceServer * s,int listen_fd)3806 SPICE_GNUC_VISIBLE int spice_server_set_listen_socket_fd(SpiceServer *s, int listen_fd)
3807 {
3808     s->config->spice_listen_socket_fd = listen_fd;
3809     return 0;
3810 }
3811 
spice_server_set_exit_on_disconnect(SpiceServer * s,int flag)3812 SPICE_GNUC_VISIBLE int spice_server_set_exit_on_disconnect(SpiceServer *s, int flag)
3813 {
3814     s->config->exit_on_disconnect = !!flag;
3815     return 0;
3816 }
3817 
spice_server_set_noauth(SpiceServer * s)3818 SPICE_GNUC_VISIBLE int spice_server_set_noauth(SpiceServer *s)
3819 {
3820     memset(s->config->taTicket.password, 0, sizeof(s->config->taTicket.password));
3821     s->config->ticketing_enabled = FALSE;
3822     return 0;
3823 }
3824 
spice_server_set_sasl(SpiceServer * s,int enabled)3825 SPICE_GNUC_VISIBLE int spice_server_set_sasl(SpiceServer *s, int enabled)
3826 {
3827 #if HAVE_SASL
3828     s->config->sasl_enabled = enabled;
3829     return 0;
3830 #else
3831     return -1;
3832 #endif
3833 }
3834 
spice_server_set_sasl_appname(SpiceServer * s,const char * appname)3835 SPICE_GNUC_VISIBLE int spice_server_set_sasl_appname(SpiceServer *s, const char *appname)
3836 {
3837 #if HAVE_SASL
3838     g_free(s->config->sasl_appname);
3839     s->config->sasl_appname = g_strdup(appname);
3840     return 0;
3841 #else
3842     return -1;
3843 #endif
3844 }
3845 
spice_server_set_name(SpiceServer * s,const char * name)3846 SPICE_GNUC_VISIBLE void spice_server_set_name(SpiceServer *s, const char *name)
3847 {
3848     g_free(s->config->spice_name);
3849     s->config->spice_name = g_strdup(name);
3850 }
3851 
spice_server_set_uuid(SpiceServer * s,const uint8_t uuid[16])3852 SPICE_GNUC_VISIBLE void spice_server_set_uuid(SpiceServer *s, const uint8_t uuid[16])
3853 {
3854     memcpy(s->config->spice_uuid, uuid, sizeof(s->config->spice_uuid));
3855     s->config->spice_uuid_is_set = TRUE;
3856 }
3857 
spice_server_set_ticket(SpiceServer * reds,const char * passwd,int lifetime,int fail_if_connected,int disconnect_if_connected)3858 SPICE_GNUC_VISIBLE int spice_server_set_ticket(SpiceServer *reds,
3859                                                const char *passwd, int lifetime,
3860                                                int fail_if_connected,
3861                                                int disconnect_if_connected)
3862 {
3863     if (reds_main_channel_connected(reds)) {
3864         if (fail_if_connected) {
3865             return -1;
3866         }
3867         if (disconnect_if_connected) {
3868             reds_disconnect(reds);
3869         }
3870     }
3871 
3872     on_activating_ticketing(reds);
3873     reds->config->ticketing_enabled = TRUE;
3874     if (lifetime == 0) {
3875         reds->config->taTicket.expiration_time = INT_MAX;
3876     } else {
3877         time_t now = spice_get_monotonic_time_ns() / NSEC_PER_SEC;
3878         reds->config->taTicket.expiration_time = now + lifetime;
3879     }
3880     if (passwd != nullptr) {
3881         if (strlen(passwd) > SPICE_MAX_PASSWORD_LENGTH)
3882             return -1;
3883         g_strlcpy(reds->config->taTicket.password, passwd, sizeof(reds->config->taTicket.password));
3884     } else {
3885         memset(reds->config->taTicket.password, 0, sizeof(reds->config->taTicket.password));
3886         reds->config->taTicket.expiration_time = 0;
3887     }
3888     return 0;
3889 }
3890 
spice_server_set_tls(SpiceServer * s,int port,const char * ca_cert_file,const char * certs_file,const char * private_key_file,const char * key_passwd,const char * dh_key_file,const char * ciphersuite)3891 SPICE_GNUC_VISIBLE int spice_server_set_tls(SpiceServer *s, int port,
3892                                             const char *ca_cert_file, const char *certs_file,
3893                                             const char *private_key_file, const char *key_passwd,
3894                                             const char *dh_key_file, const char *ciphersuite)
3895 {
3896     if (port == 0 || ca_cert_file == nullptr || certs_file == nullptr ||
3897         private_key_file == nullptr) {
3898         return -1;
3899     }
3900     if (port < 0 || port > 0xffff) {
3901         return -1;
3902     }
3903     memset(&s->config->ssl_parameters, 0, sizeof(s->config->ssl_parameters));
3904 
3905     s->config->spice_secure_port = port;
3906     g_strlcpy(s->config->ssl_parameters.ca_certificate_file, ca_cert_file,
3907               sizeof(s->config->ssl_parameters.ca_certificate_file));
3908     g_strlcpy(s->config->ssl_parameters.certs_file, certs_file,
3909               sizeof(s->config->ssl_parameters.certs_file));
3910     g_strlcpy(s->config->ssl_parameters.private_key_file, private_key_file,
3911               sizeof(s->config->ssl_parameters.private_key_file));
3912 
3913     if (key_passwd) {
3914         g_strlcpy(s->config->ssl_parameters.keyfile_password, key_passwd,
3915                   sizeof(s->config->ssl_parameters.keyfile_password));
3916     }
3917     if (ciphersuite) {
3918         g_strlcpy(s->config->ssl_parameters.ciphersuite, ciphersuite,
3919                   sizeof(s->config->ssl_parameters.ciphersuite));
3920     }
3921     if (dh_key_file) {
3922         g_strlcpy(s->config->ssl_parameters.dh_key_file, dh_key_file,
3923                   sizeof(s->config->ssl_parameters.dh_key_file));
3924     }
3925     return 0;
3926 }
3927 
spice_server_set_image_compression(SpiceServer * s,SpiceImageCompression comp)3928 SPICE_GNUC_VISIBLE int spice_server_set_image_compression(SpiceServer *s,
3929                                                           SpiceImageCompression comp)
3930 {
3931 #ifndef USE_LZ4
3932     if (comp == SPICE_IMAGE_COMPRESSION_LZ4) {
3933         spice_warning("LZ4 compression not supported, falling back to auto GLZ");
3934         comp = SPICE_IMAGE_COMPRESSION_AUTO_GLZ;
3935         reds_config_set_image_compression(s, comp);
3936         return -1;
3937     }
3938 #endif
3939     reds_config_set_image_compression(s, comp);
3940     return 0;
3941 }
3942 
spice_server_get_image_compression(SpiceServer * s)3943 SPICE_GNUC_VISIBLE SpiceImageCompression spice_server_get_image_compression(SpiceServer *s)
3944 {
3945     return s->config->image_compression;
3946 }
3947 
spice_server_set_jpeg_compression(SpiceServer * s,spice_wan_compression_t comp)3948 SPICE_GNUC_VISIBLE int spice_server_set_jpeg_compression(SpiceServer *s, spice_wan_compression_t comp)
3949 {
3950     if (comp == SPICE_WAN_COMPRESSION_INVALID) {
3951         spice_error("invalid jpeg state");
3952         return -1;
3953     }
3954     // todo: support dynamically changing the state
3955     s->config->jpeg_state = comp;
3956     return 0;
3957 }
3958 
spice_server_set_zlib_glz_compression(SpiceServer * s,spice_wan_compression_t comp)3959 SPICE_GNUC_VISIBLE int spice_server_set_zlib_glz_compression(SpiceServer *s, spice_wan_compression_t comp)
3960 {
3961     if (comp == SPICE_WAN_COMPRESSION_INVALID) {
3962         spice_error("invalid zlib_glz state");
3963         return -1;
3964     }
3965     // todo: support dynamically changing the state
3966     s->config->zlib_glz_state = comp;
3967     return 0;
3968 }
3969 
spice_server_set_channel_security(SpiceServer * s,const char * channel,int security)3970 SPICE_GNUC_VISIBLE int spice_server_set_channel_security(SpiceServer *s, const char *channel, int security)
3971 {
3972     int type;
3973     if (channel == nullptr) {
3974         s->config->default_channel_security = security;
3975         return 0;
3976     }
3977     type = red_channel_name_to_type(channel);
3978 #ifndef USE_SMARTCARD
3979     if (type == SPICE_CHANNEL_SMARTCARD) {
3980         type = -1;
3981     }
3982 #endif
3983     if (type == -1) {
3984         return -1;
3985     }
3986 
3987     reds_set_one_channel_security(s, type, security);
3988     return 0;
3989 }
3990 
3991 /* very obsolete and old function, retain only for ABI */
spice_server_get_sock_info(SpiceServer * reds,struct sockaddr * sa,socklen_t * salen)3992 SPICE_GNUC_VISIBLE int spice_server_get_sock_info(SpiceServer *reds, struct sockaddr *sa, socklen_t *salen)
3993 {
3994     return -1;
3995 }
3996 
3997 /* very obsolete and old function, retain only for ABI */
spice_server_get_peer_info(SpiceServer * reds,struct sockaddr * sa,socklen_t * salen)3998 SPICE_GNUC_VISIBLE int spice_server_get_peer_info(SpiceServer *reds, struct sockaddr *sa, socklen_t *salen)
3999 {
4000     return -1;
4001 }
4002 
spice_server_is_server_mouse(SpiceServer * reds)4003 SPICE_GNUC_VISIBLE int spice_server_is_server_mouse(SpiceServer *reds)
4004 {
4005     return reds->mouse_mode == SPICE_MOUSE_MODE_SERVER;
4006 }
4007 
spice_server_add_renderer(SpiceServer * reds,const char * name)4008 SPICE_GNUC_VISIBLE int spice_server_add_renderer(SpiceServer *reds, const char *name)
4009 {
4010     if (!reds_add_renderer(reds, name)) {
4011         return -1;
4012     }
4013     return 0;
4014 }
4015 
spice_server_set_streaming_video(SpiceServer * reds,int value)4016 SPICE_GNUC_VISIBLE int spice_server_set_streaming_video(SpiceServer *reds, int value)
4017 {
4018     if (value != SPICE_STREAM_VIDEO_OFF &&
4019         value != SPICE_STREAM_VIDEO_ALL &&
4020         value != SPICE_STREAM_VIDEO_FILTER)
4021         return -1;
4022     reds->config->streaming_video = value;
4023     reds_on_sv_change(reds);
4024     return 0;
4025 }
4026 
reds_get_streaming_video(const RedsState * reds)4027 uint32_t reds_get_streaming_video(const RedsState *reds)
4028 {
4029     return reds->config->streaming_video;
4030 }
4031 
spice_server_set_video_codecs(SpiceServer * reds,const char * video_codecs)4032 SPICE_GNUC_VISIBLE int spice_server_set_video_codecs(SpiceServer *reds, const char *video_codecs)
4033 {
4034     unsigned int installed = 0;
4035 
4036     reds_set_video_codecs_from_string(reds, video_codecs, &installed);
4037 
4038     if (!installed) {
4039         return -1;
4040     }
4041     reds_on_vc_change(reds);
4042 
4043     return 0;
4044 }
4045 
spice_server_get_video_codecs(SpiceServer * reds)4046 SPICE_GNUC_VISIBLE const char *spice_server_get_video_codecs(SpiceServer *reds)
4047 {
4048     return video_codecs_to_string(reds_get_video_codecs(reds), ";");
4049 }
4050 
spice_server_free_video_codecs(SpiceServer * reds,const char * video_codecs)4051 SPICE_GNUC_VISIBLE void spice_server_free_video_codecs(SpiceServer *reds, const char *video_codecs)
4052 {
4053     g_free((char *) video_codecs);
4054 }
4055 
reds_get_video_codecs(const RedsState * reds)4056 GArray* reds_get_video_codecs(const RedsState *reds)
4057 {
4058     return reds->config->video_codecs;
4059 }
4060 
reds_set_video_codecs(RedsState * reds,GArray * video_codecs)4061 static void reds_set_video_codecs(RedsState *reds, GArray *video_codecs)
4062 {
4063     /* The video_codecs array is immutable */
4064     g_clear_pointer(&reds->config->video_codecs, g_array_unref);
4065 
4066     spice_return_if_fail(video_codecs != nullptr);
4067 
4068     reds->config->video_codecs = video_codecs;
4069 }
4070 
spice_server_set_playback_compression(SpiceServer * reds,int enable)4071 SPICE_GNUC_VISIBLE int spice_server_set_playback_compression(SpiceServer *reds, int enable)
4072 {
4073     reds->config->playback_compression = !!enable;
4074     snd_set_playback_compression(enable);
4075     return 0;
4076 }
4077 
spice_server_set_agent_mouse(SpiceServer * reds,int enable)4078 SPICE_GNUC_VISIBLE int spice_server_set_agent_mouse(SpiceServer *reds, int enable)
4079 {
4080     reds->config->agent_mouse = enable;
4081     reds_update_mouse_mode(reds);
4082     return 0;
4083 }
4084 
spice_server_set_agent_copypaste(SpiceServer * reds,int enable)4085 SPICE_GNUC_VISIBLE int spice_server_set_agent_copypaste(SpiceServer *reds, int enable)
4086 {
4087     reds->config->agent_copypaste = enable;
4088     reds_update_agent_properties(reds);
4089     return 0;
4090 }
4091 
spice_server_set_agent_file_xfer(SpiceServer * reds,int enable)4092 SPICE_GNUC_VISIBLE int spice_server_set_agent_file_xfer(SpiceServer *reds, int enable)
4093 {
4094     reds->config->agent_file_xfer = enable;
4095     reds_update_agent_properties(reds);
4096     return 0;
4097 }
4098 
4099 /* returns FALSE if info is invalid */
reds_set_migration_dest_info(RedsState * reds,const char * dest,int port,int secure_port,const char * cert_subject)4100 static int reds_set_migration_dest_info(RedsState *reds,
4101                                         const char* dest,
4102                                         int port, int secure_port,
4103                                         const char* cert_subject)
4104 {
4105     RedsMigSpice *spice_migration = nullptr;
4106 
4107     reds_mig_release(reds->config);
4108     if ((port == -1 && secure_port == -1) || !dest) {
4109         return FALSE;
4110     }
4111 
4112     spice_migration = g_new0(RedsMigSpice, 1);
4113     spice_migration->port = port;
4114     spice_migration->sport = secure_port;
4115     spice_migration->host = g_strdup(dest);
4116     if (cert_subject) {
4117         spice_migration->cert_subject = g_strdup(cert_subject);
4118     }
4119 
4120     reds->config->mig_spice = spice_migration;
4121 
4122     return TRUE;
4123 }
4124 
4125 /* semi-seamless client migration */
spice_server_migrate_connect(SpiceServer * reds,const char * dest,int port,int secure_port,const char * cert_subject)4126 SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *reds, const char* dest,
4127                                                     int port, int secure_port,
4128                                                     const char* cert_subject)
4129 {
4130     SpiceMigrateInterface *sif;
4131     int try_seamless;
4132 
4133     spice_debug("trace");
4134     spice_assert(reds->migration_interface);
4135 
4136     if (reds->expect_migrate) {
4137         spice_debug("consecutive calls without migration. Canceling previous call");
4138         reds->main_channel->migrate_src_complete(FALSE);
4139     }
4140 
4141     sif = SPICE_UPCAST(SpiceMigrateInterface, reds->migration_interface->base.sif);
4142 
4143     if (!reds_set_migration_dest_info(reds, dest, port, secure_port, cert_subject)) {
4144         sif->migrate_connect_complete(reds->migration_interface);
4145         return -1;
4146     }
4147 
4148     reds->expect_migrate = TRUE;
4149 
4150     /*
4151      * seamless migration support was added to the client after the support in
4152      * agent_connect_tokens, so there shouldn't be contradicition - if
4153      * the client is capable of seamless migration, it is capbable of agent_connected_tokens.
4154      * The demand for agent_connected_tokens support is in order to assure that if migration
4155      * occured when the agent was not connected, the tokens state after migration will still
4156      * be valid (see reds_reset_vdp for more details).
4157      */
4158     try_seamless = reds->seamless_migration_enabled &&
4159                    reds->main_channel->test_remote_cap(SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS);
4160     /* main channel will take care of clients that are still during migration (at target)*/
4161     if (reds->main_channel->migrate_connect(reds->config->mig_spice, try_seamless)) {
4162         reds_mig_started(reds);
4163     } else {
4164         if (reds->clients.empty()) {
4165             reds_mig_release(reds->config);
4166             spice_debug("no client connected");
4167         }
4168         sif->migrate_connect_complete(reds->migration_interface);
4169     }
4170 
4171     return 0;
4172 }
4173 
spice_server_migrate_info(SpiceServer * reds,const char * dest,int port,int secure_port,const char * cert_subject)4174 SPICE_GNUC_VISIBLE int spice_server_migrate_info(SpiceServer *reds, const char* dest,
4175                                           int port, int secure_port,
4176                                           const char* cert_subject)
4177 {
4178     spice_debug("trace");
4179     spice_assert(!reds->migration_interface);
4180 
4181     if (!reds_set_migration_dest_info(reds, dest, port, secure_port, cert_subject)) {
4182         return -1;
4183     }
4184     return 0;
4185 }
4186 
spice_server_migrate_start(SpiceServer * reds)4187 SPICE_GNUC_VISIBLE int spice_server_migrate_start(SpiceServer *reds)
4188 {
4189     spice_debug("trace");
4190     if (!reds->config->mig_spice) {
4191         return -1;
4192     }
4193     return 0;
4194 }
4195 
spice_server_migrate_end(SpiceServer * reds,int completed)4196 SPICE_GNUC_VISIBLE int spice_server_migrate_end(SpiceServer *reds, int completed)
4197 {
4198     SpiceMigrateInterface *sif;
4199     int ret = 0;
4200 
4201     spice_debug("trace");
4202 
4203     spice_assert(reds->migration_interface);
4204 
4205     sif = SPICE_UPCAST(SpiceMigrateInterface, reds->migration_interface->base.sif);
4206     if (completed && !reds->expect_migrate && !reds->clients.empty()) {
4207         spice_warning("spice_server_migrate_info was not called, disconnecting clients");
4208         reds_disconnect(reds);
4209         ret = -1;
4210         goto complete;
4211     }
4212 
4213     reds->expect_migrate = FALSE;
4214     if (!reds_main_channel_connected(reds)) {
4215         spice_debug("no peer connected");
4216         goto complete;
4217     }
4218     reds_mig_finished(reds, completed);
4219     return 0;
4220 complete:
4221     if (sif->migrate_end_complete) {
4222         sif->migrate_end_complete(reds->migration_interface);
4223     }
4224     return ret;
4225 }
4226 
4227 /* interface for switch-host migration */
spice_server_migrate_switch(SpiceServer * reds)4228 SPICE_GNUC_VISIBLE int spice_server_migrate_switch(SpiceServer *reds)
4229 {
4230     spice_debug("trace");
4231     if (reds->clients.empty()) {
4232        return 0;
4233     }
4234     reds->expect_migrate = FALSE;
4235     if (!reds->config->mig_spice) {
4236         spice_warning("spice_server_migrate_switch called without migrate_info set");
4237         return 0;
4238     }
4239     reds->main_channel->migrate_switch(reds->config->mig_spice);
4240     reds_mig_release(reds->config);
4241     return 0;
4242 }
4243 
spice_server_vm_start(SpiceServer * reds)4244 SPICE_GNUC_VISIBLE void spice_server_vm_start(SpiceServer *reds)
4245 {
4246     reds->vm_running = TRUE;
4247     for (const auto& dev: reds->char_devices) {
4248         dev->start();
4249     }
4250     reds_on_vm_start(reds);
4251 }
4252 
spice_server_vm_stop(SpiceServer * reds)4253 SPICE_GNUC_VISIBLE void spice_server_vm_stop(SpiceServer *reds)
4254 {
4255     reds->vm_running = FALSE;
4256     for (const auto& dev: reds->char_devices) {
4257         dev->stop();
4258     }
4259     reds_on_vm_stop(reds);
4260 }
4261 
spice_server_set_seamless_migration(SpiceServer * reds,int enable)4262 SPICE_GNUC_VISIBLE void spice_server_set_seamless_migration(SpiceServer *reds, int enable)
4263 {
4264     /* seamless migration is not supported with multiple clients */
4265     reds->seamless_migration_enabled = enable && !reds->allow_multiple_clients;
4266     spice_debug("seamless migration enabled=%d", enable);
4267 }
4268 
reds_get_renderers(RedsState * reds)4269 GArray* reds_get_renderers(RedsState *reds)
4270 {
4271     return reds->config->renderers;
4272 }
4273 
reds_get_jpeg_state(const RedsState * reds)4274 spice_wan_compression_t reds_get_jpeg_state(const RedsState *reds)
4275 {
4276     return reds->config->jpeg_state;
4277 }
4278 
reds_get_zlib_glz_state(const RedsState * reds)4279 spice_wan_compression_t reds_get_zlib_glz_state(const RedsState *reds)
4280 {
4281     return reds->config->zlib_glz_state;
4282 }
4283 
reds_get_core_interface(RedsState * reds)4284 SpiceCoreInterfaceInternal* reds_get_core_interface(RedsState *reds)
4285 {
4286     return &reds->core;
4287 }
4288 
reds_core_watch_add(RedsState * reds,int fd,int event_mask,SpiceWatchFunc func,void * opaque)4289 SpiceWatch *reds_core_watch_add(RedsState *reds,
4290                                 int fd, int event_mask,
4291                                 SpiceWatchFunc func,
4292                                 void *opaque)
4293 {
4294    g_return_val_if_fail(reds != nullptr, NULL);
4295    g_return_val_if_fail(reds->core.watch_add != nullptr, NULL);
4296 
4297    return reds->core.watch_add(&reds->core, fd, event_mask, func, opaque);
4298 }
4299 
4300 SpiceTimer *
reds_core_timer_add_internal(RedsState * reds,SpiceTimerFunc func,void * opaque)4301 reds_core_timer_add_internal(RedsState *reds,
4302                              SpiceTimerFunc func,
4303                              void *opaque)
4304 {
4305    g_return_val_if_fail(reds != nullptr, NULL);
4306    g_return_val_if_fail(reds->core.timer_add != nullptr, NULL);
4307 
4308    return reds->core.timer_add(&reds->core, func, opaque);
4309 
4310 }
4311 
reds_update_client_mouse_allowed(RedsState * reds)4312 void reds_update_client_mouse_allowed(RedsState *reds)
4313 {
4314     int allow_now = FALSE;
4315     int x_res = 0;
4316     int y_res = 0;
4317     int num_active_workers = reds->qxl_instances.size();
4318 
4319     if (num_active_workers > 0) {
4320 
4321         allow_now = TRUE;
4322         FOREACH_QXL_INSTANCE(reds, qxl) {
4323             if (red_qxl_get_allow_client_mouse(qxl, &x_res, &y_res, &allow_now)) {
4324                 break;
4325             }
4326         }
4327     }
4328 
4329     if (allow_now || allow_now != reds->dispatcher_allows_client_mouse) {
4330         reds->monitor_mode.x_res = x_res;
4331         reds->monitor_mode.y_res = y_res;
4332         reds->dispatcher_allows_client_mouse = allow_now;
4333         reds_update_mouse_mode(reds);
4334         if (reds->is_client_mouse_allowed && reds->inputs_channel &&
4335             reds->inputs_channel->has_tablet()) {
4336             reds->inputs_channel->set_tablet_logical_size(reds->monitor_mode.x_res, reds->monitor_mode.y_res);
4337         }
4338     }
4339 }
4340 
reds_use_client_monitors_config(RedsState * reds)4341 static gboolean reds_use_client_monitors_config(RedsState *reds)
4342 {
4343     if (reds->qxl_instances.empty()) {
4344         return FALSE;
4345     }
4346 
4347     FOREACH_QXL_INSTANCE(reds, qxl) {
4348         if (!red_qxl_client_monitors_config(qxl, nullptr))
4349             return FALSE;
4350     }
4351     return TRUE;
4352 }
4353 
reds_client_monitors_config(RedsState * reds,VDAgentMonitorsConfig * monitors_config)4354 static void reds_client_monitors_config(RedsState *reds, VDAgentMonitorsConfig *monitors_config)
4355 {
4356     FOREACH_QXL_INSTANCE(reds, qxl) {
4357         if (!red_qxl_client_monitors_config(qxl, monitors_config)) {
4358             /* this is a normal condition, some qemu devices might not implement it */
4359             spice_debug("QXLInterface::client_monitors_config failed");
4360         }
4361     }
4362 }
4363 
calc_compression_level(RedsState * reds)4364 static int calc_compression_level(RedsState *reds)
4365 {
4366     spice_assert(reds_get_streaming_video(reds) != SPICE_STREAM_VIDEO_INVALID);
4367 
4368     if ((reds_get_streaming_video(reds) != SPICE_STREAM_VIDEO_OFF) ||
4369         (spice_server_get_image_compression(reds) != SPICE_IMAGE_COMPRESSION_QUIC)) {
4370         return 0;
4371     }
4372     return 1;
4373 }
4374 
reds_on_ic_change(RedsState * reds)4375 void reds_on_ic_change(RedsState *reds)
4376 {
4377     int compression_level = calc_compression_level(reds);
4378 
4379     FOREACH_QXL_INSTANCE(reds, qxl) {
4380         red_qxl_set_compression_level(qxl, compression_level);
4381         red_qxl_on_ic_change(qxl, spice_server_get_image_compression(reds));
4382     }
4383 }
4384 
reds_on_sv_change(RedsState * reds)4385 void reds_on_sv_change(RedsState *reds)
4386 {
4387     int compression_level = calc_compression_level(reds);
4388 
4389     FOREACH_QXL_INSTANCE(reds, qxl) {
4390         red_qxl_set_compression_level(qxl, compression_level);
4391         red_qxl_on_sv_change(qxl, reds_get_streaming_video(reds));
4392     }
4393 }
4394 
reds_on_vc_change(RedsState * reds)4395 void reds_on_vc_change(RedsState *reds)
4396 {
4397     FOREACH_QXL_INSTANCE(reds, qxl) {
4398         red_qxl_on_vc_change(qxl, reds_get_video_codecs(reds));
4399     }
4400 }
4401 
reds_on_vm_stop(RedsState * reds)4402 void reds_on_vm_stop(RedsState *reds)
4403 {
4404     FOREACH_QXL_INSTANCE(reds, qxl) {
4405         red_qxl_stop(qxl);
4406     }
4407 }
4408 
reds_on_vm_start(RedsState * reds)4409 void reds_on_vm_start(RedsState *reds)
4410 {
4411     FOREACH_QXL_INSTANCE(reds, qxl) {
4412         red_qxl_start(qxl);
4413     }
4414 }
4415 
reds_qxl_ram_size(RedsState * reds)4416 uint32_t reds_qxl_ram_size(RedsState *reds)
4417 {
4418     QXLInstance *first;
4419     if (reds->qxl_instances.empty()) {
4420         return 0;
4421     }
4422 
4423     first = *reds->qxl_instances.begin();
4424     return red_qxl_get_ram_size(first);
4425 }
4426 
reds_get_main_dispatcher(RedsState * reds)4427 MainDispatcher* reds_get_main_dispatcher(RedsState *reds)
4428 {
4429     return reds->main_dispatcher.get();
4430 }
4431 
RedCharDeviceVDIPort(RedsState * reds)4432 RedCharDeviceVDIPort::RedCharDeviceVDIPort(RedsState *reds):
4433     RedCharDevice(reds, nullptr, REDS_TOKENS_TO_SEND, REDS_NUM_INTERNAL_AGENT_MESSAGES)
4434 {
4435     priv->read_state = VDI_PORT_READ_STATE_READ_HEADER;
4436     priv->receive_pos = (uint8_t *)&priv->vdi_chunk_header;
4437     priv->receive_len = sizeof(priv->vdi_chunk_header);
4438 
4439     RedCharDeviceVDIPort *dev = this;
4440 
4441     agent_msg_filter_init(&dev->priv->write_filter, reds->config->agent_copypaste,
4442                           reds->config->agent_file_xfer,
4443                           reds_use_client_monitors_config(reds),
4444                           TRUE);
4445     agent_msg_filter_init(&dev->priv->read_filter, reds->config->agent_copypaste,
4446                           reds->config->agent_file_xfer,
4447                           reds_use_client_monitors_config(reds),
4448                           TRUE);
4449 }
4450 
~RedCharDeviceVDIPort()4451 RedCharDeviceVDIPort::~RedCharDeviceVDIPort()
4452 {
4453     /* make sure we have no other references to RedVDIReadBuf buffers */
4454     reset();
4455     priv->current_read_buf.reset(); // needed to pass the assert below
4456     g_free(priv->mig_data);
4457     spice_extra_assert(priv->num_read_buf == 0);
4458 }
4459 
reds_get_record(RedsState * reds)4460 RedRecord *reds_get_record(RedsState *reds)
4461 {
4462     if (reds->record) {
4463         return red_record_ref(reds->record);
4464     }
4465 
4466     return nullptr;
4467 }
4468