cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

vnc-clipboard.c (9352B)


      1/*
      2 * QEMU VNC display driver -- clipboard support
      3 *
      4 * Copyright (C) 2021 Gerd Hoffmann <kraxel@redhat.com>
      5 *
      6 * Permission is hereby granted, free of charge, to any person obtaining a copy
      7 * of this software and associated documentation files (the "Software"), to deal
      8 * in the Software without restriction, including without limitation the rights
      9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     10 * copies of the Software, and to permit persons to whom the Software is
     11 * furnished to do so, subject to the following conditions:
     12 *
     13 * The above copyright notice and this permission notice shall be included in
     14 * all copies or substantial portions of the Software.
     15 *
     16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     22 * THE SOFTWARE.
     23 */
     24
     25#include "qemu/osdep.h"
     26#include "qemu-common.h"
     27#include "vnc.h"
     28#include "vnc-jobs.h"
     29
     30static uint8_t *inflate_buffer(uint8_t *in, uint32_t in_len, uint32_t *size)
     31{
     32    z_stream stream = {
     33        .next_in  = in,
     34        .avail_in = in_len,
     35        .zalloc   = Z_NULL,
     36        .zfree    = Z_NULL,
     37    };
     38    uint32_t out_len = 8;
     39    uint8_t *out = g_malloc(out_len);
     40    int ret;
     41
     42    stream.next_out = out + stream.total_out;
     43    stream.avail_out = out_len - stream.total_out;
     44
     45    ret = inflateInit(&stream);
     46    if (ret != Z_OK) {
     47        goto err;
     48    }
     49
     50    while (stream.avail_in) {
     51        ret = inflate(&stream, Z_FINISH);
     52        switch (ret) {
     53        case Z_OK:
     54        case Z_STREAM_END:
     55            break;
     56        case Z_BUF_ERROR:
     57            out_len <<= 1;
     58            if (out_len > (1 << 20)) {
     59                goto err_end;
     60            }
     61            out = g_realloc(out, out_len);
     62            stream.next_out = out + stream.total_out;
     63            stream.avail_out = out_len - stream.total_out;
     64            break;
     65        default:
     66            goto err_end;
     67        }
     68    }
     69
     70    *size = stream.total_out;
     71    inflateEnd(&stream);
     72
     73    return out;
     74
     75err_end:
     76    inflateEnd(&stream);
     77err:
     78    g_free(out);
     79    return NULL;
     80}
     81
     82static uint8_t *deflate_buffer(uint8_t *in, uint32_t in_len, uint32_t *size)
     83{
     84    z_stream stream = {
     85        .next_in  = in,
     86        .avail_in = in_len,
     87        .zalloc   = Z_NULL,
     88        .zfree    = Z_NULL,
     89    };
     90    uint32_t out_len = 8;
     91    uint8_t *out = g_malloc(out_len);
     92    int ret;
     93
     94    stream.next_out = out + stream.total_out;
     95    stream.avail_out = out_len - stream.total_out;
     96
     97    ret = deflateInit(&stream, Z_DEFAULT_COMPRESSION);
     98    if (ret != Z_OK) {
     99        goto err;
    100    }
    101
    102    while (ret != Z_STREAM_END) {
    103        ret = deflate(&stream, Z_FINISH);
    104        switch (ret) {
    105        case Z_OK:
    106        case Z_STREAM_END:
    107            break;
    108        case Z_BUF_ERROR:
    109            out_len <<= 1;
    110            if (out_len > (1 << 20)) {
    111                goto err_end;
    112            }
    113            out = g_realloc(out, out_len);
    114            stream.next_out = out + stream.total_out;
    115            stream.avail_out = out_len - stream.total_out;
    116            break;
    117        default:
    118            goto err_end;
    119        }
    120    }
    121
    122    *size = stream.total_out;
    123    deflateEnd(&stream);
    124
    125    return out;
    126
    127err_end:
    128    deflateEnd(&stream);
    129err:
    130    g_free(out);
    131    return NULL;
    132}
    133
    134static void vnc_clipboard_send(VncState *vs, uint32_t count, uint32_t *dwords)
    135{
    136    int i;
    137
    138    vnc_lock_output(vs);
    139    vnc_write_u8(vs, VNC_MSG_SERVER_CUT_TEXT);
    140    vnc_write_u8(vs, 0);
    141    vnc_write_u8(vs, 0);
    142    vnc_write_u8(vs, 0);
    143    vnc_write_s32(vs, -(count * sizeof(uint32_t)));  /* -(message length) */
    144    for (i = 0; i < count; i++) {
    145        vnc_write_u32(vs, dwords[i]);
    146    }
    147    vnc_unlock_output(vs);
    148    vnc_flush(vs);
    149}
    150
    151static void vnc_clipboard_provide(VncState *vs,
    152                                  QemuClipboardInfo *info,
    153                                  QemuClipboardType type)
    154{
    155    uint32_t flags = 0;
    156    g_autofree uint8_t *buf = NULL;
    157    g_autofree void *zbuf = NULL;
    158    uint32_t zsize;
    159
    160    switch (type) {
    161    case QEMU_CLIPBOARD_TYPE_TEXT:
    162        flags |= VNC_CLIPBOARD_TEXT;
    163        break;
    164    default:
    165        return;
    166    }
    167    flags |= VNC_CLIPBOARD_PROVIDE;
    168
    169    buf = g_malloc(info->types[type].size + 4);
    170    buf[0] = (info->types[type].size >> 24) & 0xff;
    171    buf[1] = (info->types[type].size >> 16) & 0xff;
    172    buf[2] = (info->types[type].size >>  8) & 0xff;
    173    buf[3] = (info->types[type].size >>  0) & 0xff;
    174    memcpy(buf + 4, info->types[type].data, info->types[type].size);
    175    zbuf = deflate_buffer(buf, info->types[type].size + 4, &zsize);
    176    if (!zbuf) {
    177        return;
    178    }
    179
    180    vnc_lock_output(vs);
    181    vnc_write_u8(vs, VNC_MSG_SERVER_CUT_TEXT);
    182    vnc_write_u8(vs, 0);
    183    vnc_write_u8(vs, 0);
    184    vnc_write_u8(vs, 0);
    185    vnc_write_s32(vs, -(sizeof(uint32_t) + zsize));  /* -(message length) */
    186    vnc_write_u32(vs, flags);
    187    vnc_write(vs, zbuf, zsize);
    188    vnc_unlock_output(vs);
    189    vnc_flush(vs);
    190}
    191
    192static void vnc_clipboard_notify(Notifier *notifier, void *data)
    193{
    194    VncState *vs = container_of(notifier, VncState, cbpeer.update);
    195    QemuClipboardInfo *info = data;
    196    QemuClipboardType type;
    197    bool self_update = info->owner == &vs->cbpeer;
    198    uint32_t flags = 0;
    199
    200    if (info != vs->cbinfo) {
    201        qemu_clipboard_info_unref(vs->cbinfo);
    202        vs->cbinfo = qemu_clipboard_info_ref(info);
    203        vs->cbpending = 0;
    204        if (!self_update) {
    205            if (info->types[QEMU_CLIPBOARD_TYPE_TEXT].available) {
    206                flags |= VNC_CLIPBOARD_TEXT;
    207            }
    208            flags |= VNC_CLIPBOARD_NOTIFY;
    209            vnc_clipboard_send(vs, 1, &flags);
    210        }
    211        return;
    212    }
    213
    214    if (self_update) {
    215        return;
    216    }
    217
    218    for (type = 0; type < QEMU_CLIPBOARD_TYPE__COUNT; type++) {
    219        if (vs->cbpending & (1 << type)) {
    220            vs->cbpending &= ~(1 << type);
    221            vnc_clipboard_provide(vs, info, type);
    222        }
    223    }
    224}
    225
    226static void vnc_clipboard_request(QemuClipboardInfo *info,
    227                                  QemuClipboardType type)
    228{
    229    VncState *vs = container_of(info->owner, VncState, cbpeer);
    230    uint32_t flags = 0;
    231
    232    if (type == QEMU_CLIPBOARD_TYPE_TEXT) {
    233        flags |= VNC_CLIPBOARD_TEXT;
    234    }
    235    if (!flags) {
    236        return;
    237    }
    238    flags |= VNC_CLIPBOARD_REQUEST;
    239
    240    vnc_clipboard_send(vs, 1, &flags);
    241}
    242
    243void vnc_client_cut_text_ext(VncState *vs, int32_t len, uint32_t flags, uint8_t *data)
    244{
    245    if (flags & VNC_CLIPBOARD_CAPS) {
    246        /* need store caps somewhere ? */
    247        return;
    248    }
    249
    250    if (flags & VNC_CLIPBOARD_NOTIFY) {
    251        QemuClipboardInfo *info =
    252            qemu_clipboard_info_new(&vs->cbpeer, QEMU_CLIPBOARD_SELECTION_CLIPBOARD);
    253        if (flags & VNC_CLIPBOARD_TEXT) {
    254            info->types[QEMU_CLIPBOARD_TYPE_TEXT].available = true;
    255        }
    256        qemu_clipboard_update(info);
    257        qemu_clipboard_info_unref(info);
    258        return;
    259    }
    260
    261    if (flags & VNC_CLIPBOARD_PROVIDE &&
    262        vs->cbinfo &&
    263        vs->cbinfo->owner == &vs->cbpeer) {
    264        uint32_t size = 0;
    265        g_autofree uint8_t *buf = inflate_buffer(data, len - 4, &size);
    266        if ((flags & VNC_CLIPBOARD_TEXT) &&
    267            buf && size >= 4) {
    268            uint32_t tsize = read_u32(buf, 0);
    269            uint8_t *tbuf = buf + 4;
    270            if (tsize < size) {
    271                qemu_clipboard_set_data(&vs->cbpeer, vs->cbinfo,
    272                                        QEMU_CLIPBOARD_TYPE_TEXT,
    273                                        tsize, tbuf, true);
    274            }
    275        }
    276    }
    277
    278    if (flags & VNC_CLIPBOARD_REQUEST &&
    279        vs->cbinfo &&
    280        vs->cbinfo->owner != &vs->cbpeer) {
    281        if ((flags & VNC_CLIPBOARD_TEXT) &&
    282            vs->cbinfo->types[QEMU_CLIPBOARD_TYPE_TEXT].available) {
    283            if (vs->cbinfo->types[QEMU_CLIPBOARD_TYPE_TEXT].data) {
    284                vnc_clipboard_provide(vs, vs->cbinfo, QEMU_CLIPBOARD_TYPE_TEXT);
    285            } else {
    286                vs->cbpending |= (1 << QEMU_CLIPBOARD_TYPE_TEXT);
    287                qemu_clipboard_request(vs->cbinfo, QEMU_CLIPBOARD_TYPE_TEXT);
    288            }
    289        }
    290    }
    291}
    292
    293void vnc_client_cut_text(VncState *vs, size_t len, uint8_t *text)
    294{
    295    QemuClipboardInfo *info =
    296        qemu_clipboard_info_new(&vs->cbpeer, QEMU_CLIPBOARD_SELECTION_CLIPBOARD);
    297
    298    qemu_clipboard_set_data(&vs->cbpeer, info, QEMU_CLIPBOARD_TYPE_TEXT,
    299                            len, text, true);
    300    qemu_clipboard_info_unref(info);
    301}
    302
    303void vnc_server_cut_text_caps(VncState *vs)
    304{
    305    uint32_t caps[2];
    306
    307    if (!vnc_has_feature(vs, VNC_FEATURE_CLIPBOARD_EXT)) {
    308        return;
    309    }
    310
    311    caps[0] = (VNC_CLIPBOARD_PROVIDE |
    312               VNC_CLIPBOARD_NOTIFY  |
    313               VNC_CLIPBOARD_REQUEST |
    314               VNC_CLIPBOARD_CAPS    |
    315               VNC_CLIPBOARD_TEXT);
    316    caps[1] = 0;
    317    vnc_clipboard_send(vs, 2, caps);
    318
    319    vs->cbpeer.name = "vnc";
    320    vs->cbpeer.update.notify = vnc_clipboard_notify;
    321    vs->cbpeer.request = vnc_clipboard_request;
    322    qemu_clipboard_peer_register(&vs->cbpeer);
    323}