This extends the SPICE XML to allow channel security options
<graphics type='spice' port='-1' tlsPort='-1'
autoport='yes'>
<channel name='main' mode='secure'/>
<channel name='record' mode='insecure'/>
</graphics>
Any non-specified channel uses the default, which allows both
secure & insecure usage
* src/conf/domain_conf.c, src/conf/domain_conf.h,
src/libvirt_private.syms: Add XML syntax for specifying per
channel security options for spice.
* src/qemu/qemu_conf.c: Configure channel security with spice
---
docs/formatdomain.html.in | 15 ++++
docs/schemas/domain.rng | 21 ++++++
src/conf/domain_conf.c | 75 +++++++++++++++++++-
src/conf/domain_conf.h | 21 ++++++
src/libvirt_private.syms | 4 +
src/qemu/qemu_conf.c | 13 ++++
.../qemuxml2argv-graphics-spice.args | 2 +-
.../qemuxml2argv-graphics-spice.xml | 5 +-
8 files changed, 153 insertions(+), 3 deletions(-)
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index a4ff500..8db8b52 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -1108,6 +1108,7 @@ qemu-kvm -net nic,model=? /dev/null
</dd>
<dt><code>"spice"</code></dt>
<dd>
+ <p>
Starts a SPICE server. The <code>port</code> attribute specifies the TCP
port number (with -1 as legacy syntax indicating that it should be
auto-allocated), while <code>tlsPort</code> gives an alternative
@@ -1119,6 +1120,20 @@ qemu-kvm -net nic,model=? /dev/null
to use. It is possible to set a limit on the validity of the password
be giving an timestamp
<code>passwdValidTo='2010-04-09T15:51:00'</code>
assumed to be in UTC. NB, this may not be supported by all hypervisors.
+ </p>
+ <p>
+ When SPICE has both a normal and TLS secured TCP port configured, it
+ can be desirable to restrict what channels can be run on each port.
+ This is achieved by adding one or more <channel> elements inside
+ the main <graphics> element. Valid channel names include
+
<code>main</code>,<code>display</code>,<code>inputs</code>,<code>cursor</code>,
+ <code>playback</code>,<code>record</code>.
+ </p>
+ <pre>
+ <graphics type='spice' port='-1' tlsPort='-1'
autoport='yes'>
+ <channel name='main' mode='secure'/>
+ <channel name='record' mode='insecure'/>
+ </graphics></pre>
</dd>
<dt><code>"rdp"</code></dt>
<dd>
diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng
index 7903000..5b7ecc2 100644
--- a/docs/schemas/domain.rng
+++ b/docs/schemas/domain.rng
@@ -1093,6 +1093,27 @@
<text/>
</attribute>
</optional>
+ <zeroOrMore>
+ <element name="channel">
+ <attribute name="name">
+ <choice>
+ <value>main</value>
+ <value>display</value>
+ <value>inputs</value>
+ <value>cursor</value>
+ <value>playback</value>
+ <value>record</value>
+ </choice>
+ </attribute>
+ <attribute name="mode">
+ <choice>
+ <value>any</value>
+ <value>secure</value>
+ <value>insecure</value>
+ </choice>
+ </attribute>
+ </element>
+ </zeroOrMore>
</group>
<group>
<attribute name="type">
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index d4f8069..c1fbd65 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -271,6 +271,21 @@ VIR_ENUM_IMPL(virDomainGraphics, VIR_DOMAIN_GRAPHICS_TYPE_LAST,
"desktop",
"spice")
+VIR_ENUM_IMPL(virDomainGraphicsSpiceChannelName,
+ VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST,
+ "main",
+ "display",
+ "inputs",
+ "cursor",
+ "playback",
+ "record");
+
+VIR_ENUM_IMPL(virDomainGraphicsSpiceChannelMode,
+ VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_LAST,
+ "any",
+ "secure",
+ "insecure");
+
VIR_ENUM_IMPL(virDomainHostdevMode, VIR_DOMAIN_HOSTDEV_MODE_LAST,
"subsystem",
"capabilities")
@@ -3284,6 +3299,7 @@ virDomainGraphicsDefParseXML(xmlNodePtr node, int flags) {
def->data.desktop.display = virXMLPropString(node, "display");
} else if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) {
+ xmlNodePtr cur;
char *port = virXMLPropString(node, "port");
char *tlsPort;
char *autoport;
@@ -3328,6 +3344,40 @@ virDomainGraphicsDefParseXML(xmlNodePtr node, int flags) {
def->data.spice.keymap = virXMLPropString(node, "keymap");
if (virDomainGraphicsAuthDefParseXML(node, &def->data.vnc.auth) < 0)
goto error;
+
+ cur = node->children;
+ while (cur != NULL) {
+ if (cur->type == XML_ELEMENT_NODE) {
+ if (xmlStrEqual(cur->name, BAD_CAST "channel")) {
+ const char *name, *mode;
+ int nameval, modeval;
+ name = virXMLPropString(cur, "name");
+ mode = virXMLPropString(cur, "mode");
+
+ if (!name || !mode) {
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("spice channel missing
name/mode"));
+ goto error;
+ }
+
+ if ((nameval = virDomainGraphicsSpiceChannelNameTypeFromString(name))
< 0) {
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unknown spice channel name
%s"),
+ name);
+ goto error;
+ }
+ if ((modeval = virDomainGraphicsSpiceChannelModeTypeFromString(mode))
< 0) {
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unknown spice channel mode
%s"),
+ mode);
+ goto error;
+ }
+
+ def->data.spice.channels[nameval] = modeval;
+ }
+ }
+ cur = cur->next;
+ }
}
cleanup:
@@ -6586,6 +6636,8 @@ virDomainGraphicsDefFormat(virBufferPtr buf,
int flags)
{
const char *type = virDomainGraphicsTypeToString(def->type);
+ int children = 0;
+ int i;
if (!type) {
virDomainReportError(VIR_ERR_INTERNAL_ERROR,
@@ -6687,7 +6739,28 @@ virDomainGraphicsDefFormat(virBufferPtr buf,
}
- virBufferAddLit(buf, "/>\n");
+ if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) {
+ for (i = 0 ; i < VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST ; i++) {
+ int mode = def->data.spice.channels[i];
+ if (mode == VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_ANY)
+ continue;
+
+ if (!children) {
+ virBufferAddLit(buf, ">\n");
+ children = 1;
+ }
+
+ virBufferVSprintf(buf, " <channel name='%s'
mode='%s'/>\n",
+ virDomainGraphicsSpiceChannelNameTypeToString(i),
+ virDomainGraphicsSpiceChannelModeTypeToString(mode));
+ }
+ }
+
+ if (children) {
+ virBufferAddLit(buf, " </graphics>\n");
+ } else {
+ virBufferAddLit(buf, "/>\n");
+ }
return 0;
}
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index fababca..aca3b10 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -525,6 +525,24 @@ struct _virDomainGraphicsAuthDef {
time_t validTo; /* seconds since epoch */
};
+enum virDomainGraphicsSpiceChannelName {
+ VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MAIN,
+ VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_DISPLAY,
+ VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_INPUT,
+ VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_CURSOR,
+ VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_PLAYBACK,
+ VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_RECORD,
+
+ VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST
+};
+
+enum virDomainGraphicsSpiceChannelMode {
+ VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_ANY,
+ VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_SECURE,
+ VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_INSECURE,
+
+ VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_LAST
+};
typedef struct _virDomainGraphicsDef virDomainGraphicsDef;
typedef virDomainGraphicsDef *virDomainGraphicsDefPtr;
@@ -561,6 +579,7 @@ struct _virDomainGraphicsDef {
char *keymap;
virDomainGraphicsAuthDef auth;
unsigned int autoport :1;
+ int channels[VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST];
} spice;
} data;
};
@@ -1236,6 +1255,8 @@ VIR_ENUM_DECL(virDomainHostdevSubsys)
VIR_ENUM_DECL(virDomainInput)
VIR_ENUM_DECL(virDomainInputBus)
VIR_ENUM_DECL(virDomainGraphics)
+VIR_ENUM_DECL(virDomainGraphicsSpiceChannelName)
+VIR_ENUM_DECL(virDomainGraphicsSpiceChannelMode)
/* from libvirt.h */
VIR_ENUM_DECL(virDomainState)
VIR_ENUM_DECL(virDomainSeclabel)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 8f267f6..39eb648 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -204,6 +204,10 @@ virDomainFindByName;
virDomainFindByUUID;
virDomainGetRootFilesystem;
virDomainGraphicsDefFree;
+virDomainGraphicsSpiceChannelModeTypeFromString;
+virDomainGraphicsSpiceChannelModeTypeToString;
+virDomainGraphicsSpiceChannelNameTypeFromString;
+virDomainGraphicsSpiceChannelNameTypeToString;
virDomainGraphicsTypeFromString;
virDomainGraphicsTypeToString;
virDomainHostdevDefFree;
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
index 94ecec0..5e83655 100644
--- a/src/qemu/qemu_conf.c
+++ b/src/qemu/qemu_conf.c
@@ -5177,6 +5177,19 @@ int qemudBuildCommandLine(virConnectPtr conn,
virBufferVSprintf(&opt, ",x509-dir=%s",
driver->spiceTLSx509certdir);
+ for (i = 0 ; i < VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST ; i++) {
+ int mode = def->graphics[0]->data.spice.channels[i];
+ switch (mode) {
+ case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_SECURE:
+ virBufferVSprintf(&opt, ",tls-channel=%s",
+ virDomainGraphicsSpiceChannelNameTypeToString(i));
+ break;
+ case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_INSECURE:
+ virBufferVSprintf(&opt, ",plaintext-channel=%s",
+ virDomainGraphicsSpiceChannelNameTypeToString(i));
+ break;
+ }
+ }
if (virBufferError(&opt))
goto no_memory;
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args
b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args
index 44809b0..87b8c06 100644
--- a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args
+++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args
@@ -1 +1 @@
-LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=spice
/usr/bin/qemu -S -M pc -m 214 -smp 1 -nodefaults -monitor
unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -usb
-spice port=5903,tls-port=5904,addr=127.0.0.1,x509-dir=/etc/pki/libvirt-spice -vga qxl
-device qxl,id=video1,bus=pci.0,addr=0x4 -device
virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3
+LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=spice
/usr/bin/qemu -S -M pc -m 214 -smp 1 -nodefaults -monitor
unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -usb
-spice
port=5903,tls-port=5904,addr=127.0.0.1,x509-dir=/etc/pki/libvirt-spice,tls-channel=main,plaintext-channel=inputs
-vga qxl -device qxl,id=video1,bus=pci.0,addr=0x4 -device
virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml
b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml
index 6fe9a60..bdce04b 100644
--- a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml
+++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml
@@ -21,7 +21,10 @@
</disk>
<controller type='ide' index='0'/>
<input type='mouse' bus='ps2'/>
- <graphics type='spice' port='5903' tlsPort='5904'
autoport='no' listen='127.0.0.1'/>
+ <graphics type='spice' port='5903' tlsPort='5904'
autoport='no' listen='127.0.0.1'>
+ <channel name='main' mode='secure'/>
+ <channel name='inputs' mode='insecure'/>
+ </graphics>
<video>
<model type='qxl' vram='65536' heads='1'/>
</video>
--
1.7.2.3