Exemplo n.º 1
0
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);
}
Exemplo n.º 2
0
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);
}
Exemplo n.º 3
0
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;
}
Exemplo n.º 4
0
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;
}
Exemplo n.º 5
0
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;
}
Exemplo n.º 6
0
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);
}
Exemplo n.º 7
0
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);
}
Exemplo n.º 8
0
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;
    }
}
Exemplo n.º 9
0
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;
    }
}
Exemplo n.º 10
0
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;
}
Exemplo n.º 11
0
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;
}
Exemplo n.º 12
0
/*
 * 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;
}
Exemplo n.º 13
0
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;
}