]> Git Repo - qemu.git/blobdiff - ui/vnc.c
Merge remote-tracking branch 'remotes/mjt/tags/pull-trivial-patches-2015-09-11' into...
[qemu.git] / ui / vnc.c
index cffb5b74b3e9de5bd494b5b260f2805535057532..caf82f56f1127f0503898f3a5fc96e771f728e0e 100644 (file)
--- a/ui/vnc.c
+++ b/ui/vnc.c
 #include "trace.h"
 #include "hw/qdev.h"
 #include "sysemu/sysemu.h"
+#include "qemu/error-report.h"
 #include "qemu/sockets.h"
 #include "qemu/timer.h"
 #include "qemu/acl.h"
 #include "qemu/config-file.h"
+#include "qapi/qmp/qerror.h"
 #include "qapi/qmp/types.h"
 #include "qmp-commands.h"
 #include "qemu/osdep.h"
 #include "ui/input.h"
 #include "qapi-event.h"
+#include "crypto/hash.h"
 
 #define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT
 #define VNC_REFRESH_INTERVAL_INC  50
@@ -46,7 +49,7 @@ static const struct timeval VNC_REFRESH_STATS = { 0, 500000 };
 static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 };
 
 #include "vnc_keysym.h"
-#include "d3des.h"
+#include "crypto/cipher.h"
 
 static QTAILQ_HEAD(, VncDisplay) vnc_displays =
     QTAILQ_HEAD_INITIALIZER(vnc_displays);
@@ -353,9 +356,7 @@ static VncClientInfo *qmp_query_vnc_client(const VncState *client)
     info->base->host = g_strdup(host);
     info->base->service = g_strdup(serv);
     info->base->family = inet_netfamily(sa.ss_family);
-#ifdef CONFIG_VNC_WS
     info->base->websocket = client->websocket;
-#endif
 
 #ifdef CONFIG_VNC_TLS
     if (client->tls.session && client->tls.dname) {
@@ -427,7 +428,7 @@ VncInfo *qmp_query_vnc(Error **errp)
 
         if (getsockname(vd->lsock, (struct sockaddr *)&sa,
                         &salen) == -1) {
-            error_set(errp, QERR_UNDEFINED_ERROR);
+            error_setg(errp, QERR_UNDEFINED_ERROR);
             goto out_error;
         }
 
@@ -435,7 +436,7 @@ VncInfo *qmp_query_vnc(Error **errp)
                         host, sizeof(host),
                         serv, sizeof(serv),
                         NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
-            error_set(errp, QERR_UNDEFINED_ERROR);
+            error_setg(errp, QERR_UNDEFINED_ERROR);
             goto out_error;
         }
 
@@ -580,12 +581,10 @@ VncInfo2List *qmp_query_vnc_servers(Error **errp)
             info->server = qmp_query_server_entry(vd->lsock, false,
                                                   info->server);
         }
-#ifdef CONFIG_VNC_WS
         if (vd->lwebsock != -1) {
             info->server = qmp_query_server_entry(vd->lwebsock, true,
                                                   info->server);
         }
-#endif
 
         item = g_new0(VncInfo2List, 1);
         item->value = info;
@@ -1046,7 +1045,7 @@ static void vnc_dpy_cursor_define(DisplayChangeListener *dcl,
     }
 }
 
-static int find_and_clear_dirty_height(struct VncState *vs,
+static int find_and_clear_dirty_height(VncState *vs,
                                        int y, int last_x, int x, int height)
 {
     int h;
@@ -1213,7 +1212,7 @@ static void vnc_disconnect_start(VncState *vs)
     if (vs->csock == -1)
         return;
     vnc_set_share_mode(vs, VNC_SHARE_MODE_DISCONNECTED);
-    qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);
+    qemu_set_fd_handler(vs->csock, NULL, NULL, NULL);
     closesocket(vs->csock);
     vs->csock = -1;
 }
@@ -1229,10 +1228,8 @@ void vnc_disconnect_finish(VncState *vs)
 
     buffer_free(&vs->input);
     buffer_free(&vs->output);
-#ifdef CONFIG_VNC_WS
     buffer_free(&vs->ws_input);
     buffer_free(&vs->ws_output);
-#endif /* CONFIG_VNC_WS */
 
     qapi_free_VncClientInfo(vs->info);
 
@@ -1387,7 +1384,7 @@ static long vnc_client_write_plain(VncState *vs)
     buffer_advance(&vs->output, ret);
 
     if (vs->output.offset == 0) {
-        qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+        qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
     }
 
     return ret;
@@ -1411,12 +1408,9 @@ static void vnc_client_write_locked(void *opaque)
     } else
 #endif /* CONFIG_VNC_SASL */
     {
-#ifdef CONFIG_VNC_WS
         if (vs->encode_ws) {
             vnc_client_write_ws(vs);
-        } else
-#endif /* CONFIG_VNC_WS */
-        {
+        } else {
             vnc_client_write_plain(vs);
         }
     }
@@ -1427,14 +1421,10 @@ void vnc_client_write(void *opaque)
     VncState *vs = opaque;
 
     vnc_lock_output(vs);
-    if (vs->output.offset
-#ifdef CONFIG_VNC_WS
-            || vs->ws_output.offset
-#endif
-            ) {
+    if (vs->output.offset || vs->ws_output.offset) {
         vnc_client_write_locked(opaque);
     } else if (vs->csock != -1) {
-        qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+        qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
     }
     vnc_unlock_output(vs);
 }
@@ -1537,7 +1527,6 @@ void vnc_client_read(void *opaque)
         ret = vnc_client_read_sasl(vs);
     else
 #endif /* CONFIG_VNC_SASL */
-#ifdef CONFIG_VNC_WS
         if (vs->encode_ws) {
             ret = vnc_client_read_ws(vs);
             if (ret == -1) {
@@ -1547,10 +1536,8 @@ void vnc_client_read(void *opaque)
                 vnc_client_error(vs);
                 return;
             }
-        } else
-#endif /* CONFIG_VNC_WS */
-        {
-        ret = vnc_client_read_plain(vs);
+        } else {
+            ret = vnc_client_read_plain(vs);
         }
     if (!ret) {
         if (vs->csock == -1)
@@ -1581,7 +1568,7 @@ void vnc_write(VncState *vs, const void *data, size_t len)
     buffer_reserve(&vs->output, len);
 
     if (vs->csock != -1 && buffer_empty(&vs->output)) {
-        qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs);
+        qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, vs);
     }
 
     buffer_append(&vs->output, data, len);
@@ -1622,11 +1609,8 @@ void vnc_write_u8(VncState *vs, uint8_t value)
 void vnc_flush(VncState *vs)
 {
     vnc_lock_output(vs);
-    if (vs->csock != -1 && (vs->output.offset
-#ifdef CONFIG_VNC_WS
-                || vs->ws_output.offset
-#endif
-                )) {
+    if (vs->csock != -1 && (vs->output.offset ||
+                            vs->ws_output.offset)) {
         vnc_client_write_locked(vs);
     }
     vnc_unlock_output(vs);
@@ -2533,9 +2517,11 @@ static void make_challenge(VncState *vs)
 static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
 {
     unsigned char response[VNC_AUTH_CHALLENGE_SIZE];
-    int i, j, pwlen;
+    size_t i, pwlen;
     unsigned char key[8];
     time_t now = time(NULL);
+    QCryptoCipher *cipher = NULL;
+    Error *err = NULL;
 
     if (!vs->vd->password) {
         VNC_DEBUG("No password configured on server");
@@ -2552,9 +2538,29 @@ static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
     pwlen = strlen(vs->vd->password);
     for (i=0; i<sizeof(key); i++)
         key[i] = i<pwlen ? vs->vd->password[i] : 0;
-    deskey(key, EN0);
-    for (j = 0; j < VNC_AUTH_CHALLENGE_SIZE; j += 8)
-        des(response+j, response+j);
+
+    cipher = qcrypto_cipher_new(
+        QCRYPTO_CIPHER_ALG_DES_RFB,
+        QCRYPTO_CIPHER_MODE_ECB,
+        key, G_N_ELEMENTS(key),
+        &err);
+    if (!cipher) {
+        VNC_DEBUG("Cannot initialize cipher %s",
+                  error_get_pretty(err));
+        error_free(err);
+        goto reject;
+    }
+
+    if (qcrypto_cipher_encrypt(cipher,
+                               vs->challenge,
+                               response,
+                               VNC_AUTH_CHALLENGE_SIZE,
+                               &err) < 0) {
+        VNC_DEBUG("Cannot encrypt challenge %s",
+                  error_get_pretty(err));
+        error_free(err);
+        goto reject;
+    }
 
     /* Compare expected vs actual challenge response */
     if (memcmp(response, data, VNC_AUTH_CHALLENGE_SIZE) != 0) {
@@ -2567,6 +2573,8 @@ static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
 
         start_client_init(vs);
     }
+
+    qcrypto_cipher_free(cipher);
     return 0;
 
 reject:
@@ -2578,6 +2586,7 @@ reject:
     }
     vnc_flush(vs);
     vnc_client_error(vs);
+    qcrypto_cipher_free(cipher);
     return 0;
 }
 
@@ -2863,7 +2872,7 @@ static int vnc_refresh_server_surface(VncDisplay *vd)
                     pixman_image_get_width(vd->server));
     int height = MIN(pixman_image_get_height(vd->guest.fb),
                      pixman_image_get_height(vd->server));
-    int cmp_bytes, server_stride, min_stride, guest_stride, y = 0;
+    int cmp_bytes, server_stride, line_bytes, guest_ll, guest_stride, y = 0;
     uint8_t *guest_row0 = NULL, *server_row0;
     VncState *vs;
     int has_dirty = 0;
@@ -2882,17 +2891,21 @@ static int vnc_refresh_server_surface(VncDisplay *vd)
      * Update server dirty map.
      */
     server_row0 = (uint8_t *)pixman_image_get_data(vd->server);
-    server_stride = guest_stride = pixman_image_get_stride(vd->server);
+    server_stride = guest_stride = guest_ll =
+        pixman_image_get_stride(vd->server);
     cmp_bytes = MIN(VNC_DIRTY_PIXELS_PER_BIT * VNC_SERVER_FB_BYTES,
                     server_stride);
     if (vd->guest.format != VNC_SERVER_FB_FORMAT) {
         int width = pixman_image_get_width(vd->server);
         tmpbuf = qemu_pixman_linebuf_create(VNC_SERVER_FB_FORMAT, width);
     } else {
+        int guest_bpp =
+            PIXMAN_FORMAT_BPP(pixman_image_get_format(vd->guest.fb));
         guest_row0 = (uint8_t *)pixman_image_get_data(vd->guest.fb);
         guest_stride = pixman_image_get_stride(vd->guest.fb);
+        guest_ll = pixman_image_get_width(vd->guest.fb) * ((guest_bpp + 7) / 8);
     }
-    min_stride = MIN(server_stride, guest_stride);
+    line_bytes = MIN(server_stride, guest_ll);
 
     for (;;) {
         int x;
@@ -2923,9 +2936,10 @@ static int vnc_refresh_server_surface(VncDisplay *vd)
             if (!test_and_clear_bit(x, vd->guest.dirty[y])) {
                 continue;
             }
-            if ((x + 1) * cmp_bytes > min_stride) {
-                _cmp_bytes = min_stride - x * cmp_bytes;
+            if ((x + 1) * cmp_bytes > line_bytes) {
+                _cmp_bytes = line_bytes - x * cmp_bytes;
             }
+            assert(_cmp_bytes >= 0);
             if (memcmp(server_ptr, guest_ptr, _cmp_bytes) == 0) {
                 continue;
             }
@@ -3017,33 +3031,26 @@ static void vnc_connect(VncDisplay *vd, int csock,
     VNC_DEBUG("New client on socket %d\n", csock);
     update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
     qemu_set_nonblock(vs->csock);
-#ifdef CONFIG_VNC_WS
     if (websocket) {
         vs->websocket = 1;
 #ifdef CONFIG_VNC_TLS
         if (vd->ws_tls) {
-            qemu_set_fd_handler2(vs->csock, NULL, vncws_tls_handshake_io,
-                                 NULL, vs);
+            qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io, NULL, vs);
         } else
 #endif /* CONFIG_VNC_TLS */
         {
-            qemu_set_fd_handler2(vs->csock, NULL, vncws_handshake_read,
-                                 NULL, vs);
+            qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs);
         }
     } else
-#endif /* CONFIG_VNC_WS */
     {
-        qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+        qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
     }
 
     vnc_client_cache_addr(vs);
     vnc_qmp_event(vs, QAPI_EVENT_VNC_CONNECTED);
     vnc_set_share_mode(vs, VNC_SHARE_MODE_CONNECTING);
 
-#ifdef CONFIG_VNC_WS
-    if (!vs->websocket)
-#endif
-    {
+    if (!vs->websocket) {
         vnc_init_state(vs);
     }
 
@@ -3099,12 +3106,9 @@ static void vnc_listen_read(void *opaque, bool websocket)
 
     /* Catch-up */
     graphic_hw_update(vs->dcl.con);
-#ifdef CONFIG_VNC_WS
     if (websocket) {
         csock = qemu_accept(vs->lwebsock, (struct sockaddr *)&addr, &addrlen);
-    } else
-#endif /* CONFIG_VNC_WS */
-    {
+    } else {
         csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
     }
 
@@ -3119,12 +3123,10 @@ static void vnc_listen_regular_read(void *opaque)
     vnc_listen_read(opaque, false);
 }
 
-#ifdef CONFIG_VNC_WS
 static void vnc_listen_websocket_read(void *opaque)
 {
     vnc_listen_read(opaque, true);
 }
-#endif /* CONFIG_VNC_WS */
 
 static const DisplayChangeListenerOps dcl_ops = {
     .dpy_name             = "vnc",
@@ -3150,9 +3152,7 @@ void vnc_display_init(const char *id)
     QTAILQ_INSERT_TAIL(&vnc_displays, vs, next);
 
     vs->lsock = -1;
-#ifdef CONFIG_VNC_WS
     vs->lwebsock = -1;
-#endif
 
     QTAILQ_INIT(&vs->clients);
     vs->expires = TIME_MAX;
@@ -3182,18 +3182,16 @@ static void vnc_display_close(VncDisplay *vs)
     vs->enabled = false;
     vs->is_unix = false;
     if (vs->lsock != -1) {
-        qemu_set_fd_handler2(vs->lsock, NULL, NULL, NULL, NULL);
+        qemu_set_fd_handler(vs->lsock, NULL, NULL, NULL);
         close(vs->lsock);
         vs->lsock = -1;
     }
-#ifdef CONFIG_VNC_WS
     vs->ws_enabled = false;
     if (vs->lwebsock != -1) {
-        qemu_set_fd_handler2(vs->lwebsock, NULL, NULL, NULL, NULL);
+        qemu_set_fd_handler(vs->lwebsock, NULL, NULL, NULL);
         close(vs->lwebsock);
         vs->lwebsock = -1;
     }
-#endif /* CONFIG_VNC_WS */
     vs->auth = VNC_AUTH_INVALID;
     vs->subauth = VNC_AUTH_INVALID;
 #ifdef CONFIG_VNC_TLS
@@ -3482,7 +3480,14 @@ void vnc_display_open(const char *id, Error **errp)
 
     h = strrchr(vnc, ':');
     if (h) {
-        char *host = g_strndup(vnc, h - vnc);
+        char *host;
+        size_t hlen = h - vnc;
+
+        if (vnc[0] == '[' && vnc[hlen - 1] == ']') {
+            host = g_strndup(vnc + 1, hlen - 2);
+        } else {
+            host = g_strndup(vnc, hlen);
+        }
         qemu_opt_set(sopts, "host", host, &error_abort);
         qemu_opt_set(wsopts, "host", host, &error_abort);
         qemu_opt_set(sopts, "port", h+1, &error_abort);
@@ -3509,12 +3514,20 @@ void vnc_display_open(const char *id, Error **errp)
     }
 
     password = qemu_opt_get_bool(opts, "password", false);
-    if (password && fips_get_state()) {
-        error_setg(errp,
-                   "VNC password auth disabled due to FIPS mode, "
-                   "consider using the VeNCrypt or SASL authentication "
-                   "methods as an alternative");
-        goto fail;
+    if (password) {
+        if (fips_get_state()) {
+            error_setg(errp,
+                       "VNC password auth disabled due to FIPS mode, "
+                       "consider using the VeNCrypt or SASL authentication "
+                       "methods as an alternative");
+            goto fail;
+        }
+        if (!qcrypto_cipher_supports(
+                QCRYPTO_CIPHER_ALG_DES_RFB)) {
+            error_setg(errp,
+                       "Cipher backend does not support DES RFB algorithm");
+            goto fail;
+        }
     }
 
     reverse = qemu_opt_get_bool(opts, "reverse", false);
@@ -3572,13 +3585,12 @@ void vnc_display_open(const char *id, Error **errp)
 
     websocket = qemu_opt_get(opts, "websocket");
     if (websocket) {
-#ifdef CONFIG_VNC_WS
         vs->ws_enabled = true;
         qemu_opt_set(wsopts, "port", websocket, &error_abort);
-#else /* ! CONFIG_VNC_WS */
-        error_setg(errp, "Websockets protocol requires gnutls support");
-        goto fail;
-#endif /* ! CONFIG_VNC_WS */
+        if (!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) {
+            error_setg(errp, "SHA1 hash support is required for websockets");
+            goto fail;
+        }
     }
 
 #ifdef CONFIG_VNC_JPEG
@@ -3602,10 +3614,6 @@ void vnc_display_open(const char *id, Error **errp)
             aclname = g_strdup_printf("vnc.%s.x509dname", vs->id);
         }
         vs->tls.acl = qemu_acl_init(aclname);
-        if (!vs->tls.acl) {
-            fprintf(stderr, "Failed to create x509 dname ACL\n");
-            exit(1);
-        }
         g_free(aclname);
     }
 #endif
@@ -3619,10 +3627,6 @@ void vnc_display_open(const char *id, Error **errp)
             aclname = g_strdup_printf("vnc.%s.username", vs->id);
         }
         vs->sasl.acl = qemu_acl_init(aclname);
-        if (!vs->sasl.acl) {
-            fprintf(stderr, "Failed to create username ACL\n");
-            exit(1);
-        }
         g_free(aclname);
     }
 #endif
@@ -3669,9 +3673,7 @@ void vnc_display_open(const char *id, Error **errp)
         /* connect to viewer */
         int csock;
         vs->lsock = -1;
-#ifdef CONFIG_VNC_WS
         vs->lwebsock = -1;
-#endif
         if (strncmp(vnc, "unix:", 5) == 0) {
             csock = unix_connect(vnc+5, errp);
         } else {
@@ -3685,13 +3687,15 @@ void vnc_display_open(const char *id, Error **errp)
         /* listen for connects */
         if (strncmp(vnc, "unix:", 5) == 0) {
             vs->lsock = unix_listen(vnc+5, NULL, 0, errp);
+            if (vs->lsock < 0) {
+                goto fail;
+            }
             vs->is_unix = true;
         } else {
             vs->lsock = inet_listen_opts(sopts, 5900, errp);
             if (vs->lsock < 0) {
                 goto fail;
             }
-#ifdef CONFIG_VNC_WS
             if (vs->ws_enabled) {
                 vs->lwebsock = inet_listen_opts(wsopts, 0, errp);
                 if (vs->lwebsock < 0) {
@@ -3702,17 +3706,13 @@ void vnc_display_open(const char *id, Error **errp)
                     goto fail;
                 }
             }
-#endif /* CONFIG_VNC_WS */
         }
         vs->enabled = true;
-        qemu_set_fd_handler2(vs->lsock, NULL,
-                vnc_listen_regular_read, NULL, vs);
-#ifdef CONFIG_VNC_WS
+        qemu_set_fd_handler(vs->lsock, vnc_listen_regular_read, NULL, vs);
         if (vs->ws_enabled) {
-            qemu_set_fd_handler2(vs->lwebsock, NULL,
-                    vnc_listen_websocket_read, NULL, vs);
+            qemu_set_fd_handler(vs->lwebsock, vnc_listen_websocket_read,
+                                NULL, vs);
         }
-#endif /* CONFIG_VNC_WS */
     }
     qemu_opts_del(sopts);
     qemu_opts_del(wsopts);
@@ -3722,9 +3722,7 @@ fail:
     qemu_opts_del(sopts);
     qemu_opts_del(wsopts);
     vs->enabled = false;
-#ifdef CONFIG_VNC_WS
     vs->ws_enabled = false;
-#endif /* CONFIG_VNC_WS */
 }
 
 void vnc_display_add_client(const char *id, int csock, bool skipauth)
@@ -3750,10 +3748,10 @@ static void vnc_auto_assign_id(QemuOptsList *olist, QemuOpts *opts)
     qemu_opts_set_id(opts, id);
 }
 
-QemuOpts *vnc_parse_func(const char *str)
+QemuOpts *vnc_parse(const char *str, Error **errp)
 {
     QemuOptsList *olist = qemu_find_opts("vnc");
-    QemuOpts *opts = qemu_opts_parse(olist, str, 1);
+    QemuOpts *opts = qemu_opts_parse(olist, str, true, errp);
     const char *id;
 
     if (!opts) {
@@ -3768,7 +3766,7 @@ QemuOpts *vnc_parse_func(const char *str)
     return opts;
 }
 
-int vnc_init_func(QemuOpts *opts, void *opaque)
+int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp)
 {
     Error *local_err = NULL;
     char *id = (char *)qemu_opts_id(opts);
@@ -3777,8 +3775,7 @@ int vnc_init_func(QemuOpts *opts, void *opaque)
     vnc_display_init(id);
     vnc_display_open(id, &local_err);
     if (local_err != NULL) {
-        error_report("Failed to start VNC server on `%s': %s",
-                     qemu_opt_get(opts, "display"),
+        error_report("Failed to start VNC server: %s",
                      error_get_pretty(local_err));
         error_free(local_err);
         exit(1);
This page took 0.042643 seconds and 4 git commands to generate.