From c448e8552b0f6135dabddf944a7110f929c08320 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 11 Mar 2010 11:13:32 -0300 Subject: [PATCH 1/5] spice: tls support Add options to the -spice command line switch to setup tls. --- qemu-config.c | 24 ++++++++++++++++++ qemu-options.hx | 18 ++++++++++++- ui/spice-core.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 104 insertions(+), 5 deletions(-) diff --git a/qemu-config.c b/qemu-config.c index 32917cbda4..26748a5b7a 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -361,12 +361,36 @@ QemuOptsList qemu_spice_opts = { { .name = "port", .type = QEMU_OPT_NUMBER, + },{ + .name = "tls-port", + .type = QEMU_OPT_NUMBER, },{ .name = "password", .type = QEMU_OPT_STRING, },{ .name = "disable-ticketing", .type = QEMU_OPT_BOOL, + },{ + .name = "x509-dir", + .type = QEMU_OPT_STRING, + },{ + .name = "x509-key-file", + .type = QEMU_OPT_STRING, + },{ + .name = "x509-key-password", + .type = QEMU_OPT_STRING, + },{ + .name = "x509-cert-file", + .type = QEMU_OPT_STRING, + },{ + .name = "x509-cacert-file", + .type = QEMU_OPT_STRING, + },{ + .name = "x509-dh-key-file", + .type = QEMU_OPT_STRING, + },{ + .name = "tls-ciphers", + .type = QEMU_OPT_STRING, }, { /* end if list */ } }, diff --git a/qemu-options.hx b/qemu-options.hx index 718d47a2a3..9d3f8ef529 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -680,7 +680,7 @@ Enable the spice remote desktop protocol. Valid options are @table @option @item port= -Set the TCP port spice is listening on. +Set the TCP port spice is listening on for plaintext channels. @item password= Set the password you need to authenticate. @@ -688,6 +688,22 @@ Set the password you need to authenticate. @item disable-ticketing Allow client connects without authentication. +@item tls-port= +Set the TCP port spice is listening on for encrypted channels. + +@item x509-dir= +Set the x509 file directory. Expects same filenames as -vnc $display,x509=$dir + +@item x509-key-file= +@item x509-key-password= +@item x509-cert-file= +@item x509-cacert-file= +@item x509-dh-key-file= +The x509 file names can also be configured individually. + +@item tls-ciphers= +Specify which ciphers to use. + @end table ETEXI diff --git a/ui/spice-core.c b/ui/spice-core.c index 8b5e4a8096..51aa782ca7 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -22,6 +22,7 @@ #include "qemu-spice.h" #include "qemu-timer.h" #include "qemu-queue.h" +#include "qemu-x509.h" #include "monitor.h" /* core bits */ @@ -141,20 +142,74 @@ static SpiceCoreInterface core_interface = { void qemu_spice_init(void) { QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head); - const char *password; - int port; + const char *password, *str, *x509_dir, + *x509_key_password = NULL, + *x509_dh_file = NULL, + *tls_ciphers = NULL; + char *x509_key_file = NULL, + *x509_cert_file = NULL, + *x509_cacert_file = NULL; + int port, tls_port, len; if (!opts) { return; } port = qemu_opt_get_number(opts, "port", 0); - if (!port) { + tls_port = qemu_opt_get_number(opts, "tls-port", 0); + if (!port && !tls_port) { return; } password = qemu_opt_get(opts, "password"); + if (tls_port) { + x509_dir = qemu_opt_get(opts, "x509-dir"); + if (NULL == x509_dir) { + x509_dir = "."; + } + len = strlen(x509_dir) + 32; + + str = qemu_opt_get(opts, "x509-key-file"); + if (str) { + x509_key_file = qemu_strdup(str); + } else { + x509_key_file = qemu_malloc(len); + snprintf(x509_key_file, len, "%s/%s", x509_dir, X509_SERVER_KEY_FILE); + } + + str = qemu_opt_get(opts, "x509-cert-file"); + if (str) { + x509_cert_file = qemu_strdup(str); + } else { + x509_cert_file = qemu_malloc(len); + snprintf(x509_cert_file, len, "%s/%s", x509_dir, X509_SERVER_CERT_FILE); + } + + str = qemu_opt_get(opts, "x509-cacert-file"); + if (str) { + x509_cacert_file = qemu_strdup(str); + } else { + x509_cacert_file = qemu_malloc(len); + snprintf(x509_cacert_file, len, "%s/%s", x509_dir, X509_CA_CERT_FILE); + } + + x509_key_password = qemu_opt_get(opts, "x509-key-password"); + x509_dh_file = qemu_opt_get(opts, "x509-dh-file"); + tls_ciphers = qemu_opt_get(opts, "tls-ciphers"); + } + spice_server = spice_server_new(); - spice_server_set_port(spice_server, port); + if (port) { + spice_server_set_port(spice_server, port); + } + if (tls_port) { + spice_server_set_tls(spice_server, tls_port, + x509_cacert_file, + x509_cert_file, + x509_key_file, + x509_key_password, + x509_dh_file, + tls_ciphers); + } if (password) { spice_server_set_ticket(spice_server, password, 0, 0, 0); } @@ -169,6 +224,10 @@ void qemu_spice_init(void) using_spice = 1; qemu_spice_input_init(); + + qemu_free(x509_key_file); + qemu_free(x509_cert_file); + qemu_free(x509_cacert_file); } int qemu_spice_add_interface(SpiceBaseInstance *sin) From 9f04e09e36e430dd57c69c88b0532e9dc5061a47 Mon Sep 17 00:00:00 2001 From: Yonit Halperin Date: Wed, 14 Jul 2010 13:26:34 +0300 Subject: [PATCH 2/5] spice: make compression configurable. This patch adds options to the -spice command line switch to configure image compression. [ v2: speling fix in the documentation ] --- qemu-config.c | 9 ++++++ qemu-options.hx | 9 ++++++ ui/spice-core.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 93 insertions(+), 2 deletions(-) diff --git a/qemu-config.c b/qemu-config.c index 26748a5b7a..8b545b15f9 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -391,6 +391,15 @@ QemuOptsList qemu_spice_opts = { },{ .name = "tls-ciphers", .type = QEMU_OPT_STRING, + },{ + .name = "image-compression", + .type = QEMU_OPT_STRING, + },{ + .name = "jpeg-wan-compression", + .type = QEMU_OPT_STRING, + },{ + .name = "zlib-glz-wan-compression", + .type = QEMU_OPT_STRING, }, { /* end if list */ } }, diff --git a/qemu-options.hx b/qemu-options.hx index 9d3f8ef529..b9edaaed4a 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -704,6 +704,15 @@ The x509 file names can also be configured individually. @item tls-ciphers= Specify which ciphers to use. +@item image-compression=[auto_glz|auto_lz|quic|glz|lz|off] +Configure image compression (lossless). +Default is auto_glz. + +@item jpeg-wan-compression=[auto|never|always] +@item zlib-glz-wan-compression=[auto|never|always] +Configure wan image compression (lossy for slow links). +Default is auto. + @end table ETEXI diff --git a/ui/spice-core.c b/ui/spice-core.c index 51aa782ca7..156704681c 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -137,6 +137,59 @@ static SpiceCoreInterface core_interface = { .watch_remove = watch_remove, }; +/* config string parsing */ + +static int name2enum(const char *string, const char *table[], int entries) +{ + int i; + + if (string) { + for (i = 0; i < entries; i++) { + if (!table[i]) { + continue; + } + if (strcmp(string, table[i]) != 0) { + continue; + } + return i; + } + } + return -1; +} + +static int parse_name(const char *string, const char *optname, + const char *table[], int entries) +{ + int value = name2enum(string, table, entries); + + if (value != -1) { + return value; + } + fprintf(stderr, "spice: invalid %s: %s\n", optname, string); + exit(1); +} + +static const char *compression_names[] = { + [ SPICE_IMAGE_COMPRESS_OFF ] = "off", + [ SPICE_IMAGE_COMPRESS_AUTO_GLZ ] = "auto_glz", + [ SPICE_IMAGE_COMPRESS_AUTO_LZ ] = "auto_lz", + [ SPICE_IMAGE_COMPRESS_QUIC ] = "quic", + [ SPICE_IMAGE_COMPRESS_GLZ ] = "glz", + [ SPICE_IMAGE_COMPRESS_LZ ] = "lz", +}; +#define parse_compression(_name) \ + parse_name(_name, "image compression", \ + compression_names, ARRAY_SIZE(compression_names)) + +static const char *wan_compression_names[] = { + [ SPICE_WAN_COMPRESSION_AUTO ] = "auto", + [ SPICE_WAN_COMPRESSION_NEVER ] = "never", + [ SPICE_WAN_COMPRESSION_ALWAYS ] = "always", +}; +#define parse_wan_compression(_name) \ + parse_name(_name, "wan compression", \ + wan_compression_names, ARRAY_SIZE(wan_compression_names)) + /* functions for the rest of qemu */ void qemu_spice_init(void) @@ -150,6 +203,8 @@ void qemu_spice_init(void) *x509_cert_file = NULL, *x509_cacert_file = NULL; int port, tls_port, len; + spice_image_compression_t compression; + spice_wan_compression_t wan_compr; if (!opts) { return; @@ -217,8 +272,26 @@ void qemu_spice_init(void) spice_server_set_noauth(spice_server); } - /* TODO: make configurable via cmdline */ - spice_server_set_image_compression(spice_server, SPICE_IMAGE_COMPRESS_AUTO_GLZ); + compression = SPICE_IMAGE_COMPRESS_AUTO_GLZ; + str = qemu_opt_get(opts, "image-compression"); + if (str) { + compression = parse_compression(str); + } + spice_server_set_image_compression(spice_server, compression); + + wan_compr = SPICE_WAN_COMPRESSION_AUTO; + str = qemu_opt_get(opts, "jpeg-wan-compression"); + if (str) { + wan_compr = parse_wan_compression(str); + } + spice_server_set_jpeg_compression(spice_server, wan_compr); + + wan_compr = SPICE_WAN_COMPRESSION_AUTO; + str = qemu_opt_get(opts, "zlib-glz-wan-compression"); + if (str) { + wan_compr = parse_wan_compression(str); + } + spice_server_set_zlib_glz_compression(spice_server, wan_compr); spice_server_init(spice_server, &core_interface); using_spice = 1; From 17b6dea08bd8c8484bc48dc67add236d2fe002b5 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 27 Aug 2010 14:09:56 +0200 Subject: [PATCH 3/5] spice: add config options for channel security. This allows to enforce tls or plaintext usage for certain spice channels. [ v2: code style fixup ] --- qemu-config.c | 6 ++++++ qemu-options.hx | 8 ++++++++ ui/spice-core.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/qemu-config.c b/qemu-config.c index 8b545b15f9..f52e50c377 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -391,6 +391,12 @@ QemuOptsList qemu_spice_opts = { },{ .name = "tls-ciphers", .type = QEMU_OPT_STRING, + },{ + .name = "tls-channel", + .type = QEMU_OPT_STRING, + },{ + .name = "plaintext-channel", + .type = QEMU_OPT_STRING, },{ .name = "image-compression", .type = QEMU_OPT_STRING, diff --git a/qemu-options.hx b/qemu-options.hx index b9edaaed4a..5a0d26b474 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -704,6 +704,14 @@ The x509 file names can also be configured individually. @item tls-ciphers= Specify which ciphers to use. +@item tls-channel=[main|display|inputs|record|playback|tunnel] +@item plaintext-channel=[main|display|inputs|record|playback|tunnel] +Force specific channel to be used with or without TLS encryption. The +options can be specified multiple times to configure multiple +channels. The special name "default" can be used to set the default +mode. For channels which are not explicitly forced into one mode the +spice client is allowed to pick tls/plaintext as he pleases. + @item image-compression=[auto_glz|auto_lz|quic|glz|lz|off] Configure image compression (lossless). Default is auto_glz. diff --git a/ui/spice-core.c b/ui/spice-core.c index 156704681c..7664ef7700 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -192,6 +192,32 @@ static const char *wan_compression_names[] = { /* functions for the rest of qemu */ +static int add_channel(const char *name, const char *value, void *opaque) +{ + int security = 0; + int rc; + + if (strcmp(name, "tls-channel") == 0) { + security = SPICE_CHANNEL_SECURITY_SSL; + } + if (strcmp(name, "plaintext-channel") == 0) { + security = SPICE_CHANNEL_SECURITY_NONE; + } + if (security == 0) { + return 0; + } + if (strcmp(value, "default") == 0) { + rc = spice_server_set_channel_security(spice_server, NULL, security); + } else { + rc = spice_server_set_channel_security(spice_server, value, security); + } + if (rc != 0) { + fprintf(stderr, "spice: failed to set channel security for %s\n", value); + exit(1); + } + return 0; +} + void qemu_spice_init(void) { QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head); @@ -293,6 +319,8 @@ void qemu_spice_init(void) } spice_server_set_zlib_glz_compression(spice_server, wan_compr); + qemu_opt_foreach(opts, add_channel, NULL, 0); + spice_server_init(spice_server, &core_interface); using_spice = 1; From 333b0eebcc8941b8453e837293eaa1191e967c25 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 27 Aug 2010 14:29:16 +0200 Subject: [PATCH 4/5] spice: add config options for the listening address Make listening address configurable. Also add options to force using IPv4 or IPv6. --- qemu-config.c | 9 +++++++++ qemu-options.hx | 7 +++++++ ui/spice-core.c | 13 +++++++++++-- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/qemu-config.c b/qemu-config.c index f52e50c377..5a62ae131d 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -364,6 +364,15 @@ QemuOptsList qemu_spice_opts = { },{ .name = "tls-port", .type = QEMU_OPT_NUMBER, + },{ + .name = "addr", + .type = QEMU_OPT_STRING, + },{ + .name = "ipv4", + .type = QEMU_OPT_BOOL, + },{ + .name = "ipv6", + .type = QEMU_OPT_BOOL, },{ .name = "password", .type = QEMU_OPT_STRING, diff --git a/qemu-options.hx b/qemu-options.hx index 5a0d26b474..0410b90dce 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -682,6 +682,13 @@ Enable the spice remote desktop protocol. Valid options are @item port= Set the TCP port spice is listening on for plaintext channels. +@item addr= +Set the IP address spice is listening on. Default is any address. + +@item ipv4 +@item ipv6 +Force using the specified IP version. + @item password= Set the password you need to authenticate. diff --git a/ui/spice-core.c b/ui/spice-core.c index 7664ef7700..6f613c6324 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -221,14 +221,14 @@ static int add_channel(const char *name, const char *value, void *opaque) void qemu_spice_init(void) { QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head); - const char *password, *str, *x509_dir, + const char *password, *str, *x509_dir, *addr, *x509_key_password = NULL, *x509_dh_file = NULL, *tls_ciphers = NULL; char *x509_key_file = NULL, *x509_cert_file = NULL, *x509_cacert_file = NULL; - int port, tls_port, len; + int port, tls_port, len, addr_flags; spice_image_compression_t compression; spice_wan_compression_t wan_compr; @@ -278,7 +278,16 @@ void qemu_spice_init(void) tls_ciphers = qemu_opt_get(opts, "tls-ciphers"); } + addr = qemu_opt_get(opts, "addr"); + addr_flags = 0; + if (qemu_opt_get_bool(opts, "ipv4", 0)) { + addr_flags |= SPICE_ADDR_FLAG_IPV4_ONLY; + } else if (qemu_opt_get_bool(opts, "ipv6", 0)) { + addr_flags |= SPICE_ADDR_FLAG_IPV6_ONLY; + } + spice_server = spice_server_new(); + spice_server_set_addr(spice_server, addr ? addr : "", addr_flags); if (port) { spice_server_set_port(spice_server, port); } From 84a23f251fe85768338434040257bb96cf555de8 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 30 Aug 2010 16:36:53 +0200 Subject: [PATCH 5/5] spice: add misc config options This patch adds a few more options to tweak spice server behavior. The documentation update chunk has the details ;) --- qemu-config.c | 9 +++++++++ qemu-options.hx | 9 +++++++++ ui/spice-core.c | 29 ++++++++++++++++++++++++++++- 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/qemu-config.c b/qemu-config.c index 5a62ae131d..52f18bef31 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -415,6 +415,15 @@ QemuOptsList qemu_spice_opts = { },{ .name = "zlib-glz-wan-compression", .type = QEMU_OPT_STRING, + },{ + .name = "streaming-video", + .type = QEMU_OPT_STRING, + },{ + .name = "agent-mouse", + .type = QEMU_OPT_BOOL, + },{ + .name = "playback-compression", + .type = QEMU_OPT_BOOL, }, { /* end if list */ } }, diff --git a/qemu-options.hx b/qemu-options.hx index 0410b90dce..9e38dfb775 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -728,6 +728,15 @@ Default is auto_glz. Configure wan image compression (lossy for slow links). Default is auto. +@item streaming-video=[off|all|filter] +Configure video stream detection. Default is filter. + +@item agent-mouse=[on|off] +Enable/disable passing mouse events via vdagent. Default is on. + +@item playback-compression=[on|off] +Enable/disable audio stream compression (using celt 0.5.1). Default is on. + @end table ETEXI diff --git a/ui/spice-core.c b/ui/spice-core.c index 6f613c6324..6a1cf17e49 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -169,6 +169,18 @@ static int parse_name(const char *string, const char *optname, exit(1); } +#if SPICE_SERVER_VERSION >= 0x000600 /* 0.6.0 */ + +static const char *stream_video_names[] = { + [ SPICE_STREAM_VIDEO_OFF ] = "off", + [ SPICE_STREAM_VIDEO_ALL ] = "all", + [ SPICE_STREAM_VIDEO_FILTER ] = "filter", +}; +#define parse_stream_video(_name) \ + name2enum(_name, stream_video_names, ARRAY_SIZE(stream_video_names)) + +#endif /* >= 0.6.0 */ + static const char *compression_names[] = { [ SPICE_IMAGE_COMPRESS_OFF ] = "off", [ SPICE_IMAGE_COMPRESS_AUTO_GLZ ] = "auto_glz", @@ -228,7 +240,7 @@ void qemu_spice_init(void) char *x509_key_file = NULL, *x509_cert_file = NULL, *x509_cacert_file = NULL; - int port, tls_port, len, addr_flags; + int port, tls_port, len, addr_flags, streaming_video; spice_image_compression_t compression; spice_wan_compression_t wan_compr; @@ -328,6 +340,21 @@ void qemu_spice_init(void) } spice_server_set_zlib_glz_compression(spice_server, wan_compr); +#if SPICE_SERVER_VERSION >= 0x000600 /* 0.6.0 */ + + str = qemu_opt_get(opts, "streaming-video"); + if (str) { + streaming_video = parse_stream_video(str); + spice_server_set_streaming_video(spice_server, streaming_video); + } + + spice_server_set_agent_mouse + (spice_server, qemu_opt_get_bool(opts, "agent-mouse", 1)); + spice_server_set_playback_compression + (spice_server, qemu_opt_get_bool(opts, "playback-compression", 1)); + +#endif /* >= 0.6.0 */ + qemu_opt_foreach(opts, add_channel, NULL, 0); spice_server_init(spice_server, &core_interface);