void vnc_display_open(const char *id, Error **errp) { VncDisplay *vs = vnc_display_find(id); QemuOpts *opts = qemu_opts_find(&qemu_vnc_opts, id); QemuOpts *sopts, *wsopts; const char *share, *device_id; QemuConsole *con; bool password = false; bool reverse = false; const char *vnc; const char *has_to; char *h; bool has_ipv4 = false; bool has_ipv6 = false; const char *websocket; bool tls = false, x509 = false; #ifdef CONFIG_VNC_TLS const char *path; #endif bool sasl = false; #ifdef CONFIG_VNC_SASL int saslErr; #endif #if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL) int acl = 0; #endif int lock_key_sync = 1; if (!vs) { error_setg(errp, "VNC display not active"); return; } vnc_display_close(vs); if (!opts) { return; } vnc = qemu_opt_get(opts, "vnc"); if (!vnc || strcmp(vnc, "none") == 0) { return; } sopts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort); wsopts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort); h = strrchr(vnc, ':'); if (h) { char *host = g_strndup(vnc, h - vnc); 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); g_free(host); } else { error_setg(errp, "no vnc port specified"); goto fail; } has_to = qemu_opt_get(opts, "to"); has_ipv4 = qemu_opt_get_bool(opts, "ipv4", false); has_ipv6 = qemu_opt_get_bool(opts, "ipv6", false); if (has_to) { qemu_opt_set(sopts, "to", has_to, &error_abort); qemu_opt_set(wsopts, "to", has_to, &error_abort); } if (has_ipv4) { qemu_opt_set(sopts, "ipv4", "on", &error_abort); qemu_opt_set(wsopts, "ipv4", "on", &error_abort); } if (has_ipv6) { qemu_opt_set(sopts, "ipv6", "on", &error_abort); qemu_opt_set(wsopts, "ipv6", "on", &error_abort); } 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; } reverse = qemu_opt_get_bool(opts, "reverse", false); lock_key_sync = qemu_opt_get_bool(opts, "lock-key-sync", true); sasl = qemu_opt_get_bool(opts, "sasl", false); #ifndef CONFIG_VNC_SASL if (sasl) { error_setg(errp, "VNC SASL auth requires cyrus-sasl support"); goto fail; } #endif /* CONFIG_VNC_SASL */ tls = qemu_opt_get_bool(opts, "tls", false); #ifdef CONFIG_VNC_TLS path = qemu_opt_get(opts, "x509"); if (!path) { path = qemu_opt_get(opts, "x509verify"); if (path) { vs->tls.x509verify = true; } } if (path) { x509 = true; if (vnc_tls_set_x509_creds_dir(vs, path) < 0) { error_setg(errp, "Failed to find x509 certificates/keys in %s", path); goto fail; } } #else /* ! CONFIG_VNC_TLS */ if (tls) { error_setg(errp, "VNC TLS auth requires gnutls support"); goto fail; } #endif /* ! CONFIG_VNC_TLS */ #if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL) acl = qemu_opt_get_bool(opts, "acl", false); #endif share = qemu_opt_get(opts, "share"); if (share) { if (strcmp(share, "ignore") == 0) { vs->share_policy = VNC_SHARE_POLICY_IGNORE; } else if (strcmp(share, "allow-exclusive") == 0) { vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE; } else if (strcmp(share, "force-shared") == 0) { vs->share_policy = VNC_SHARE_POLICY_FORCE_SHARED; } else { error_setg(errp, "unknown vnc share= option"); goto fail; } } else { vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE; } vs->connections_limit = qemu_opt_get_number(opts, "connections", 32); 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 */ } #ifdef CONFIG_VNC_JPEG vs->lossy = qemu_opt_get_bool(opts, "lossy", false); #endif vs->non_adaptive = qemu_opt_get_bool(opts, "non-adaptive", false); /* adaptive updates are only used with tight encoding and * if lossy updates are enabled so we can disable all the * calculations otherwise */ if (!vs->lossy) { vs->non_adaptive = true; } #ifdef CONFIG_VNC_TLS if (acl && x509 && vs->tls.x509verify) { char *aclname; if (strcmp(vs->id, "default") == 0) { aclname = g_strdup("vnc.x509dname"); } else { 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 #ifdef CONFIG_VNC_SASL if (acl && sasl) { char *aclname; if (strcmp(vs->id, "default") == 0) { aclname = g_strdup("vnc.username"); } else { 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 vnc_display_setup_auth(vs, password, sasl, tls, x509); #ifdef CONFIG_VNC_SASL if ((saslErr = sasl_server_init(NULL, "qemu")) != SASL_OK) { error_setg(errp, "Failed to initialize SASL auth: %s", sasl_errstring(saslErr, NULL, NULL)); goto fail; } #endif vs->lock_key_sync = lock_key_sync; device_id = qemu_opt_get(opts, "display"); if (device_id) { DeviceState *dev; int head = qemu_opt_get_number(opts, "head", 0); dev = qdev_find_recursive(sysbus_get_default(), device_id); if (dev == NULL) { error_setg(errp, "Device '%s' not found", device_id); goto fail; } con = qemu_console_lookup_by_device(dev, head); if (con == NULL) { error_setg(errp, "Device %s is not bound to a QemuConsole", device_id); goto fail; } } else { con = NULL; } if (con != vs->dcl.con) { unregister_displaychangelistener(&vs->dcl); vs->dcl.con = con; register_displaychangelistener(&vs->dcl); } if (reverse) { /* 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 { csock = inet_connect(vnc, errp); } if (csock < 0) { goto fail; } vnc_connect(vs, csock, false, false); } else { /* listen for connects */ if (strncmp(vnc, "unix:", 5) == 0) { vs->lsock = unix_listen(vnc+5, NULL, 0, errp); 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) { if (vs->lsock != -1) { close(vs->lsock); vs->lsock = -1; } 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 if (vs->ws_enabled) { qemu_set_fd_handler2(vs->lwebsock, NULL, vnc_listen_websocket_read, NULL, vs); } #endif /* CONFIG_VNC_WS */ } qemu_opts_del(sopts); qemu_opts_del(wsopts); return; 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_open(const char *id, Error **errp) { VncDisplay *vs = vnc_display_find(id); QemuOpts *opts = qemu_opts_find(&qemu_vnc_opts, id); QemuOpts *sopts, *wsopts; const char *share, *device_id; QemuConsole *con; bool password = false; bool reverse = false; const char *vnc; const char *has_to; char *h; bool has_ipv4 = false; bool has_ipv6 = false; const char *websocket; bool tls = false, x509 = false; #ifdef CONFIG_VNC_TLS const char *path; #endif bool sasl = false; #ifdef CONFIG_VNC_SASL int saslErr; #endif #if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL) int acl = 0; #endif int lock_key_sync = 1; if (!vs) { error_setg(errp, "VNC display not active"); return; } vnc_display_close(vs); if (!opts) { return; } vnc = qemu_opt_get(opts, "vnc"); if (!vnc || strcmp(vnc, "none") == 0) { return; } sopts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort); wsopts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort); h = strrchr(vnc, ':'); if (h) { char *host = g_strndup(vnc, h - vnc); 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); g_free(host); } else { error_setg(errp, "no vnc port specified"); goto fail; } has_to = qemu_opt_get(opts, "to"); has_ipv4 = qemu_opt_get_bool(opts, "ipv4", false); has_ipv6 = qemu_opt_get_bool(opts, "ipv6", false); if (has_to) { qemu_opt_set(sopts, "to", has_to, &error_abort); qemu_opt_set(wsopts, "to", has_to, &error_abort); } if (has_ipv4) { qemu_opt_set(sopts, "ipv4", "on", &error_abort); qemu_opt_set(wsopts, "ipv4", "on", &error_abort); } if (has_ipv6) { qemu_opt_set(sopts, "ipv6", "on", &error_abort); qemu_opt_set(wsopts, "ipv6", "on", &error_abort); } 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; } reverse = qemu_opt_get_bool(opts, "reverse", false); lock_key_sync = qemu_opt_get_bool(opts, "lock-key-sync", true); sasl = qemu_opt_get_bool(opts, "sasl", false); #ifndef CONFIG_VNC_SASL if (sasl) { error_setg(errp, "VNC SASL auth requires cyrus-sasl support"); goto fail; } #endif   tls = qemu_opt_get_bool(opts, "tls", false); #ifdef CONFIG_VNC_TLS path = qemu_opt_get(opts, "x509"); if (!path) { path = qemu_opt_get(opts, "x509verify"); if (path) { vs->tls.x509verify = true; } } if (path) { x509 = true; if (vnc_tls_set_x509_creds_dir(vs, path) < 0) { error_setg(errp, "Failed to find x509 certificates/keys in %s", path); goto fail; } } #else   if (tls) { error_setg(errp, "VNC TLS auth requires gnutls support"); goto fail; } #endif   #if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL) acl = qemu_opt_get_bool(opts, "acl", false); #endif share = qemu_opt_get(opts, "share"); if (share) { if (strcmp(share, "ignore") == 0) { vs->share_policy = VNC_SHARE_POLICY_IGNORE; } else if (strcmp(share, "allow-exclusive") == 0) { vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE; } else if (strcmp(share, "force-shared") == 0) { vs->share_policy = VNC_SHARE_POLICY_FORCE_SHARED; } else { error_setg(errp, "unknown vnc share= option"); goto fail; } } else { vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE; } vs->connections_limit = qemu_opt_get_number(opts, "connections", 32); 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   error_setg(errp, "Websockets protocol requires gnutls support"); goto fail; #endif   } #ifdef CONFIG_VNC_JPEG vs->lossy = qemu_opt_get_bool(opts, "lossy", false); #endif vs->non_adaptive = qemu_opt_get_bool(opts, "non-adaptive", false);   if (!vs->lossy) { vs->non_adaptive = true; } #ifdef CONFIG_VNC_TLS if (acl && x509 && vs->tls.x509verify) { char *aclname; if (strcmp(vs->id, "default") == 0) { aclname = g_strdup("vnc.x509dname"); } else { 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 #ifdef CONFIG_VNC_SASL if (acl && sasl) { char *aclname; if (strcmp(vs->id, "default") == 0) { aclname = g_strdup("vnc.username"); } else { 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 vnc_display_setup_auth(vs, password, sasl, tls, x509); #ifdef CONFIG_VNC_SASL if ((saslErr = sasl_server_init(NULL, "qemu")) != SASL_OK) { error_setg(errp, "Failed to initialize SASL auth: %s", sasl_errstring(saslErr, NULL, NULL)); goto fail; } #endif vs->lock_key_sync = lock_key_sync; device_id = qemu_opt_get(opts, "display"); if (device_id) { DeviceState *dev; int head = qemu_opt_get_number(opts, "head", 0); dev = qdev_find_recursive(sysbus_get_default(), device_id); if (dev == NULL) { error_setg(errp, "Device '%s' not found", device_id); goto fail; } con = qemu_console_lookup_by_device(dev, head); if (con == NULL) { error_setg(errp, "Device %s is not bound to a QemuConsole", device_id); goto fail; } } else { con = NULL; } if (con != vs->dcl.con) { unregister_displaychangelistener(&vs->dcl); vs->dcl.con = con; register_displaychangelistener(&vs->dcl); } if (reverse) {   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 { csock = inet_connect(vnc, errp); } if (csock < 0) { goto fail; } vnc_connect(vs, csock, false, false); } else {   if (strncmp(vnc, "unix:", 5) == 0) { vs->lsock = unix_listen(vnc+5, NULL, 0, errp); 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) { if (vs->lsock != -1) { close(vs->lsock); vs->lsock = -1; } goto fail; } } #endif   } vs->enabled = true; qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_regular_read, NULL, vs); #ifdef CONFIG_VNC_WS if (vs->ws_enabled) { qemu_set_fd_handler2(vs->lwebsock, NULL, vnc_listen_websocket_read, NULL, vs); } #endif   } qemu_opts_del(sopts); qemu_opts_del(wsopts); return; fail: qemu_opts_del(sopts); qemu_opts_del(wsopts); vs->enabled = false; #ifdef CONFIG_VNC_WS vs->ws_enabled = false; #endif   }