void red_dispatcher_async_complete(struct RedDispatcher *dispatcher, AsyncCommand *async_command) { pthread_mutex_lock(&dispatcher->async_lock); ring_remove(&async_command->link); spice_debug("%p: cookie %" PRId64, async_command, async_command->cookie); if (ring_is_empty(&dispatcher->async_commands)) { spice_debug("no more async commands"); } pthread_mutex_unlock(&dispatcher->async_lock); switch (async_command->message) { case RED_WORKER_MESSAGE_UPDATE_ASYNC: break; case RED_WORKER_MESSAGE_ADD_MEMSLOT_ASYNC: break; case RED_WORKER_MESSAGE_DESTROY_SURFACES_ASYNC: break; case RED_WORKER_MESSAGE_CREATE_PRIMARY_SURFACE_ASYNC: red_dispatcher_create_primary_surface_complete(dispatcher); break; case RED_WORKER_MESSAGE_DESTROY_PRIMARY_SURFACE_ASYNC: red_dispatcher_destroy_primary_surface_complete(dispatcher); break; case RED_WORKER_MESSAGE_DESTROY_SURFACE_WAIT_ASYNC: break; case RED_WORKER_MESSAGE_FLUSH_SURFACES_ASYNC: break; case RED_WORKER_MESSAGE_MONITORS_CONFIG_ASYNC: break; default: spice_warning("unexpected message %d", async_command->message); } dispatcher->qxl->st->qif->async_complete(dispatcher->qxl, async_command->cookie); free(async_command); }
void cursor_channel_init(CursorChannel *cursor, CursorChannelClient *client) { spice_return_if_fail(cursor); if (!red_channel_is_connected(&cursor->common.base) || COMMON_CHANNEL(cursor)->during_target_migrate) { spice_debug("during_target_migrate: skip init"); return; } if (client) red_channel_client_pipe_add_type(RED_CHANNEL_CLIENT(client), PIPE_ITEM_TYPE_CURSOR_INIT); else red_channel_pipes_add_type(RED_CHANNEL(cursor), PIPE_ITEM_TYPE_CURSOR_INIT); }
static void smartcard_device_state_restore_partial_read(SmartCardDeviceState *state, SpiceMigrateDataSmartcard *mig_data) { uint8_t *read_data; spice_debug("read_size %u", mig_data->read_size); read_data = (uint8_t *)mig_data + mig_data->read_data_ptr - sizeof(SpiceMigrateDataHeader); if (mig_data->read_size < sizeof(VSCMsgHeader)) { spice_assert(state->buf_size >= mig_data->read_size); } else { smartcard_read_buf_prepare(state, (VSCMsgHeader *)read_data); } memcpy(state->buf, read_data, mig_data->read_size); state->buf_used = mig_data->read_size; state->buf_pos = state->buf + mig_data->read_size; }
bool reds_sasl_handle_auth_mechlen(RedsStream *stream, AsyncReadDone read_cb, void *opaque) { RedsSASL *sasl = &stream->priv->sasl; if (sasl->len < 1 || sasl->len > 100) { spice_warning("Got bad client mechname len %d", sasl->len); return false; } sasl->mechname = spice_malloc(sasl->len + 1); spice_debug("Wait for client mechname"); reds_stream_async_read(stream, (uint8_t *)sasl->mechname, sasl->len, read_cb, opaque); return true; }
RedsSaslError reds_sasl_handle_auth_startlen(RedsStream *stream, AsyncReadDone read_cb, void *opaque) { RedsSASL *sasl = &stream->priv->sasl; spice_debug("Got client start len %d", sasl->len); if (sasl->len > SASL_DATA_MAX_LEN) { spice_warning("Too much SASL data %d", sasl->len); return REDS_SASL_ERROR_INVALID_DATA; } if (sasl->len == 0) { return REDS_SASL_ERROR_RETRY; } sasl->data = spice_realloc(sasl->data, sasl->len); reds_stream_async_read(stream, (uint8_t *)sasl->data, sasl->len, read_cb, opaque); return REDS_SASL_ERROR_OK; }
static int smartcard_channel_client_handle_migrate_data(RedChannelClient *rcc, uint32_t size, void *message) { SmartCardChannelClient *scc; SpiceMigrateDataHeader *header; SpiceMigrateDataSmartcard *mig_data; scc = SPICE_CONTAINEROF(rcc, SmartCardChannelClient, base); header = (SpiceMigrateDataHeader *)message; mig_data = (SpiceMigrateDataSmartcard *)(header + 1); if (size < sizeof(SpiceMigrateDataHeader) + sizeof(SpiceMigrateDataSmartcard)) { spice_error("bad message size"); return FALSE; } if (!migration_protocol_validate_header(header, SPICE_MIGRATE_DATA_SMARTCARD_MAGIC, SPICE_MIGRATE_DATA_SMARTCARD_VERSION)) { spice_error("bad header"); return FALSE; } if (!mig_data->base.connected) { /* client wasn't attached to a smartcard */ return TRUE; } if (!scc->smartcard_state) { SpiceCharDeviceInstance *char_device = smartcard_readers_get_unattached(); if (!char_device) { spice_warning("no unattached device available"); return TRUE; } else { smartcard_char_device_attach_client(char_device, scc); } } spice_debug("reader added %d partial read_size %u", mig_data->reader_added, mig_data->read_size); scc->smartcard_state->reader_added = mig_data->reader_added; smartcard_device_state_restore_partial_read(scc->smartcard_state, mig_data); return spice_char_device_state_restore(scc->smartcard_state->chardev_st, &mig_data->base); }
static void smartcard_char_device_notify_reader_remove(SmartCardDeviceState *st) { SpiceCharDeviceWriteBuffer *write_buf; VSCMsgHeader *vheader; if (!st->reader_added) { spice_debug("reader add was never sent to the device"); return; } write_buf = spice_char_device_write_buffer_get(st->chardev_st, NULL, sizeof(vheader)); if (!write_buf) { spice_error("failed to allocate write buffer"); return; } st->reader_added = FALSE; vheader = (VSCMsgHeader *)write_buf->buf; vheader->type = VSC_ReaderRemove; vheader->reader_id = st->reader_id; vheader->length = 0; smartcard_channel_write_to_reader(write_buf); }
RedsSaslError reds_sasl_handle_auth_steplen(RedsStream *stream, AsyncReadDone read_cb, void *opaque) { RedsSASL *sasl = &stream->priv->sasl; spice_debug("Got steplen %d", sasl->len); if (sasl->len > SASL_DATA_MAX_LEN) { spice_warning("Too much SASL data %d", sasl->len); return REDS_SASL_ERROR_INVALID_DATA; } if (sasl->len == 0) { read_cb(opaque); /* FIXME: can't report potential errors correctly here, * but read_cb() will have done the needed RedLinkInfo cleanups * if an error occurs, so the caller should not need to do more * treatment */ return REDS_SASL_ERROR_OK; } else { sasl->data = spice_realloc(sasl->data, sasl->len); reds_stream_async_read(stream, (uint8_t *)sasl->data, sasl->len, read_cb, opaque); return REDS_SASL_ERROR_OK; } }
static SpiceCharDeviceMsgToClient *spicevmc_chardev_read_msg_from_dev(SpiceCharDeviceInstance *sin, void *opaque) { SpiceVmcState *state = opaque; SpiceCharDeviceInterface *sif; SpiceVmcPipeItem *msg_item; int n; sif = SPICE_CONTAINEROF(sin->base.sif, SpiceCharDeviceInterface, base); if (!state->rcc) { return NULL; } if (!state->pipe_item) { msg_item = spice_new0(SpiceVmcPipeItem, 1); msg_item->refs = 1; red_channel_pipe_item_init(&state->channel, &msg_item->base, PIPE_ITEM_TYPE_SPICEVMC_DATA); } else { spice_assert(state->pipe_item->buf_used == 0); msg_item = state->pipe_item; state->pipe_item = NULL; } n = sif->read(sin, msg_item->buf, sizeof(msg_item->buf)); if (n > 0) { spice_debug("read from dev %d", n); msg_item->buf_used = n; return msg_item; } else { state->pipe_item = msg_item; return NULL; } }
RedsSaslError reds_sasl_handle_auth_start(RedsStream *stream, AsyncReadDone read_cb, void *opaque) { const char *serverout; unsigned int serveroutlen; int err; char *clientdata = NULL; RedsSASL *sasl = &stream->priv->sasl; uint32_t datalen = sasl->len; /* NB, distinction of NULL vs "" is *critical* in SASL */ if (datalen) { clientdata = sasl->data; clientdata[datalen - 1] = '\0'; /* Should be on wire, but make sure */ datalen--; /* Don't count NULL byte when passing to _start() */ } spice_debug("Start SASL auth with mechanism %s. Data %p (%d bytes)", sasl->mechlist, clientdata, datalen); err = sasl_server_start(sasl->conn, sasl->mechlist, clientdata, datalen, &serverout, &serveroutlen); if (err != SASL_OK && err != SASL_CONTINUE) { spice_warning("sasl start failed %d (%s)", err, sasl_errdetail(sasl->conn)); return REDS_SASL_ERROR_INVALID_DATA; } if (serveroutlen > SASL_DATA_MAX_LEN) { spice_warning("sasl start reply data too long %d", serveroutlen); return REDS_SASL_ERROR_INVALID_DATA; } spice_debug("SASL return data %d bytes, %p", serveroutlen, serverout); if (serveroutlen) { serveroutlen += 1; reds_stream_write_all(stream, &serveroutlen, sizeof(uint32_t)); reds_stream_write_all(stream, serverout, serveroutlen); } else { reds_stream_write_all(stream, &serveroutlen, sizeof(uint32_t)); } /* Whether auth is complete */ reds_stream_write_u8(stream, err == SASL_CONTINUE ? 0 : 1); if (err == SASL_CONTINUE) { spice_debug("%s", "Authentication must continue (start)"); /* Wait for step length */ reds_stream_async_read(stream, (uint8_t *)&sasl->len, sizeof(uint32_t), read_cb, opaque); return REDS_SASL_ERROR_CONTINUE; } else { int ssf; if (auth_sasl_check_ssf(sasl, &ssf) == 0) { spice_warning("Authentication rejected for weak SSF"); goto authreject; } spice_debug("Authentication successful"); reds_stream_write_u32(stream, SPICE_LINK_ERR_OK); /* Accept auth */ /* * Delay writing in SSF encoded until now */ sasl->runSSF = ssf; reds_stream_disable_writev(stream); /* make sure writev isn't called directly anymore */ return REDS_SASL_ERROR_OK; } authreject: reds_stream_write_u32(stream, 1); /* Reject auth */ reds_stream_write_u32(stream, sizeof("Authentication failed")); reds_stream_write_all(stream, "Authentication failed", sizeof("Authentication failed")); return REDS_SASL_ERROR_AUTH_FAILED; }
bool reds_sasl_start_auth(RedsStream *stream, AsyncReadDone read_cb, void *opaque) { const char *mechlist = NULL; sasl_security_properties_t secprops; int err; char *localAddr, *remoteAddr; int mechlistlen; RedsSASL *sasl = &stream->priv->sasl; if (!(localAddr = reds_stream_get_local_address(stream))) { goto error; } if (!(remoteAddr = reds_stream_get_remote_address(stream))) { free(localAddr); goto error; } err = sasl_server_new("spice", NULL, /* FQDN - just delegates to gethostname */ NULL, /* User realm */ localAddr, remoteAddr, NULL, /* Callbacks, not needed */ SASL_SUCCESS_DATA, &sasl->conn); free(localAddr); free(remoteAddr); localAddr = remoteAddr = NULL; if (err != SASL_OK) { spice_warning("sasl context setup failed %d (%s)", err, sasl_errstring(err, NULL, NULL)); sasl->conn = NULL; goto error; } /* Inform SASL that we've got an external SSF layer from TLS */ if (stream->priv->ssl) { sasl_ssf_t ssf; ssf = SSL_get_cipher_bits(stream->priv->ssl, NULL); err = sasl_setprop(sasl->conn, SASL_SSF_EXTERNAL, &ssf); if (err != SASL_OK) { spice_warning("cannot set SASL external SSF %d (%s)", err, sasl_errstring(err, NULL, NULL)); goto error_dispose; } } else { sasl->wantSSF = 1; } memset(&secprops, 0, sizeof secprops); /* Inform SASL that we've got an external SSF layer from TLS */ if (stream->priv->ssl) { /* If we've got TLS (or UNIX domain sock), we don't care about SSF */ secprops.min_ssf = 0; secprops.max_ssf = 0; secprops.maxbufsize = 8192; secprops.security_flags = 0; } else { /* Plain TCP, better get an SSF layer */ secprops.min_ssf = 56; /* Good enough to require kerberos */ secprops.max_ssf = 100000; /* Arbitrary big number */ secprops.maxbufsize = 8192; /* Forbid any anonymous or trivially crackable auth */ secprops.security_flags = SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT; } err = sasl_setprop(sasl->conn, SASL_SEC_PROPS, &secprops); if (err != SASL_OK) { spice_warning("cannot set SASL security props %d (%s)", err, sasl_errstring(err, NULL, NULL)); goto error_dispose; } err = sasl_listmech(sasl->conn, NULL, /* Don't need to set user */ "", /* Prefix */ ",", /* Separator */ "", /* Suffix */ &mechlist, NULL, NULL); if (err != SASL_OK || mechlist == NULL) { spice_warning("cannot list SASL mechanisms %d (%s)", err, sasl_errdetail(sasl->conn)); goto error_dispose; } spice_debug("Available mechanisms for client: '%s'", mechlist); sasl->mechlist = spice_strdup(mechlist); mechlistlen = strlen(mechlist); if (!reds_stream_write_all(stream, &mechlistlen, sizeof(uint32_t)) || !reds_stream_write_all(stream, sasl->mechlist, mechlistlen)) { spice_warning("SASL mechanisms write error"); goto error; } spice_debug("Wait for client mechname length"); reds_stream_async_read(stream, (uint8_t *)&sasl->len, sizeof(uint32_t), read_cb, opaque); return true; error_dispose: sasl_dispose(&sasl->conn); sasl->conn = NULL; error: return false; }
/* * read_safe * helper. reads until size bytes accumulated in buf, if an error other then * EINTR is encountered returns -1, otherwise returns 0. * @block if 1 the read will block (the fd is always blocking). * if 0 poll first, return immediately if no bytes available, otherwise * read size in blocking mode. */ static int read_safe(int fd, uint8_t *buf, size_t size, int block) { int read_size = 0; int ret; struct pollfd pollfd = {.fd = fd, .events = POLLIN, .revents = 0}; if (size == 0) { return 0; } if (!block) { while ((ret = poll(&pollfd, 1, 0)) == -1) { if (errno == EINTR) { spice_debug("EINTR in poll"); continue; } spice_error("poll failed"); return -1; } if (!(pollfd.revents & POLLIN)) { return 0; } } while (read_size < size) { ret = read(fd, buf + read_size, size - read_size); if (ret == -1) { if (errno == EINTR) { spice_debug("EINTR in read"); continue; } return -1; } if (ret == 0) { spice_error("broken pipe on read"); return -1; } read_size += ret; } return read_size; } /* * write_safe * @return -1 for error, otherwise number of written bytes. may be zero. */ static int write_safe(int fd, uint8_t *buf, size_t size) { int written_size = 0; int ret; while (written_size < size) { ret = write(fd, buf + written_size, size - written_size); if (ret == -1) { if (errno != EINTR) { spice_debug("EINTR in write"); return -1; } continue; } written_size += ret; } return written_size; }
RedDispatcher *red_dispatcher_init(QXLInstance *qxl) { RedDispatcher *red_dispatcher; RedWorkerMessage message; WorkerInitData init_data; QXLDevInitInfo init_info; int r; RedChannel *display_channel; RedChannel *cursor_channel; sigset_t thread_sig_mask; sigset_t curr_sig_mask; ClientCbs client_cbs = { NULL, }; quic_init(); sw_canvas_init(); #ifdef USE_OPENGL gl_canvas_init(); #endif // USE_OPENGL red_dispatcher = spice_new0(RedDispatcher, 1); ring_init(&red_dispatcher->async_commands); spice_debug("red_dispatcher->async_commands.next %p", red_dispatcher->async_commands.next); dispatcher_init(&red_dispatcher->dispatcher, RED_WORKER_MESSAGE_COUNT, NULL); init_data.qxl = red_dispatcher->qxl = qxl; init_data.id = qxl->id; init_data.red_dispatcher = red_dispatcher; init_data.pending = &red_dispatcher->pending; init_data.num_renderers = num_renderers; memcpy(init_data.renderers, renderers, sizeof(init_data.renderers)); pthread_mutex_init(&red_dispatcher->async_lock, NULL); init_data.image_compression = image_compression; init_data.jpeg_state = jpeg_state; init_data.zlib_glz_state = zlib_glz_state; init_data.streaming_video = streaming_video; red_dispatcher->base.major_version = SPICE_INTERFACE_QXL_MAJOR; red_dispatcher->base.minor_version = SPICE_INTERFACE_QXL_MINOR; red_dispatcher->base.wakeup = qxl_worker_wakeup; red_dispatcher->base.oom = qxl_worker_oom; red_dispatcher->base.start = qxl_worker_start; red_dispatcher->base.stop = qxl_worker_stop; red_dispatcher->base.update_area = qxl_worker_update_area; red_dispatcher->base.add_memslot = qxl_worker_add_memslot; red_dispatcher->base.del_memslot = qxl_worker_del_memslot; red_dispatcher->base.reset_memslots = qxl_worker_reset_memslots; red_dispatcher->base.destroy_surfaces = qxl_worker_destroy_surfaces; red_dispatcher->base.create_primary_surface = qxl_worker_create_primary_surface; red_dispatcher->base.destroy_primary_surface = qxl_worker_destroy_primary_surface; red_dispatcher->base.reset_image_cache = qxl_worker_reset_image_cache; red_dispatcher->base.reset_cursor = qxl_worker_reset_cursor; red_dispatcher->base.destroy_surface_wait = qxl_worker_destroy_surface_wait; red_dispatcher->base.loadvm_commands = qxl_worker_loadvm_commands; qxl->st->qif->get_init_info(qxl, &init_info); init_data.memslot_id_bits = init_info.memslot_id_bits; init_data.memslot_gen_bits = init_info.memslot_gen_bits; init_data.num_memslots = init_info.num_memslots; init_data.num_memslots_groups = init_info.num_memslots_groups; init_data.internal_groupslot_id = init_info.internal_groupslot_id; init_data.n_surfaces = init_info.n_surfaces; num_active_workers = 1; sigfillset(&thread_sig_mask); sigdelset(&thread_sig_mask, SIGILL); sigdelset(&thread_sig_mask, SIGFPE); sigdelset(&thread_sig_mask, SIGSEGV); pthread_sigmask(SIG_SETMASK, &thread_sig_mask, &curr_sig_mask); if ((r = pthread_create(&red_dispatcher->worker_thread, NULL, red_worker_main, &init_data))) { spice_error("create thread failed %d", r); } pthread_sigmask(SIG_SETMASK, &curr_sig_mask, NULL); read_message(red_dispatcher->dispatcher.send_fd, &message); spice_assert(message == RED_WORKER_MESSAGE_READY); display_channel = red_dispatcher_display_channel_create(red_dispatcher); if (display_channel) { client_cbs.connect = red_dispatcher_set_display_peer; client_cbs.disconnect = red_dispatcher_disconnect_display_peer; client_cbs.migrate = red_dispatcher_display_migrate; red_channel_register_client_cbs(display_channel, &client_cbs); red_channel_set_data(display_channel, red_dispatcher); red_channel_set_cap(display_channel, SPICE_DISPLAY_CAP_MONITORS_CONFIG); reds_register_channel(display_channel); } cursor_channel = red_dispatcher_cursor_channel_create(red_dispatcher); if (cursor_channel) { client_cbs.connect = red_dispatcher_set_cursor_peer; client_cbs.disconnect = red_dispatcher_disconnect_cursor_peer; client_cbs.migrate = red_dispatcher_cursor_migrate; red_channel_register_client_cbs(cursor_channel, &client_cbs); red_channel_set_data(cursor_channel, red_dispatcher); reds_register_channel(cursor_channel); } qxl->st->qif->attache_worker(qxl, &red_dispatcher->base); qxl->st->qif->set_compression_level(qxl, calc_compression_level()); red_dispatcher->next = dispatchers; dispatchers = red_dispatcher; return red_dispatcher; }