[libvirt] [PATCHv3 0/5] smartcard: round 3

This series has hopefully taken into account all the feedback from v2 (https://www.redhat.com/archives/libvir-list/2011-January/msg00608.html). Major changes: - enhance the XML to support optional ccid <controller> (missing controllers are added according to <address> elements) and optional <address> per smartcard (missing address assume the next available port on controller 0) - enhance the XML to support an optional <source dev='/path'/> for host mode. For now, this path is only used in SELinux labeling; I suspect that this needs more work, since the point is that a single device in the host should be shared among the NSS implementation of multiple guests (so labeling the host device to belong to a single guest is wrong); but fixing it correctly requires a better understanding of what NSS actually needs to access, as well as possibly modifying qemu's smartcard implementation to take the host device either as a pathname or even as an already-opened fd. - enhance the XML to support an optional <database> element for host-certificates mode. - enhance the qemu command line to fully populate all parameters, rather than the bare minimum defaults, and reflect that in the tests. It requires this pre-requisite patch for qemu -chardev aliases: https://www.redhat.com/archives/libvir-list/2011-January/msg01032.html Eric Blake (5): smartcard: add XML support for <smartcard> device smartcard: add domain conf support smartcard: check for qemu capability smartcard: enable SELinux support smartcard: turn on qemu support cfg.mk | 1 + docs/formatdomain.html.in | 95 +++++- docs/schemas/domain.rng | 73 ++++ src/conf/domain_conf.c | 396 +++++++++++++++++++- src/conf/domain_conf.h | 53 +++- src/libvirt_private.syms | 4 + src/qemu/qemu_capabilities.c | 2 + src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_command.c | 90 +++++- src/security/security_selinux.c | 94 +++++ .../qemuxml2argv-smartcard-controller.args | 1 + .../qemuxml2argv-smartcard-controller.xml | 20 + .../qemuxml2argv-smartcard-host-certificates.args | 1 + .../qemuxml2argv-smartcard-host-certificates.xml | 20 + .../qemuxml2argv-smartcard-host.args | 1 + .../qemuxml2argv-smartcard-host.xml | 16 + .../qemuxml2argv-smartcard-passthrough-tcp.args | 1 + .../qemuxml2argv-smartcard-passthrough-tcp.xml | 19 + tests/qemuxml2argvtest.c | 13 + 19 files changed, 887 insertions(+), 14 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.xml -- 1.7.3.5

Assuming a hypervisor that supports multiple smartcard devices in the guest, this would be a valid XML description: <devices> <smartcard mode='host'/> <smartcard mode='host-certificates'> <certificate>/path/to/cert1</certificate> <certificate>/path/to/cert2</certificate> <certificate>/path/to/cert3</certificate> </smartcard> <smartcard mode='passthrough' type='tcp'> <source mode='connect' host='127.0.0.1' service='2001'/> <protocol type='raw'/> </smartcard> </devices> * docs/formatdomain.html.in (Smartcard devices): New section. * docs/schemas/domain.rng (smartcard): New define, used in devices. * tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.xml: New file to test schema. * tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.xml: Likewise. * tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.xml: Likewise. * tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.xml: Likewise. Notes: v2: incoporate feedback from RFC, make passthrough type default to tcp v3: add optional <address> per <smartcard>, enhance <controller> to understand ccid, add optional <database> in mode hosts-certificates, optional <source> in mode hosts, add another test file, revert v2 passthrough mode back to requiring explicit type --- docs/formatdomain.html.in | 95 +++++++++++++++++++- docs/schemas/domain.rng | 73 +++++++++++++++ .../qemuxml2argv-smartcard-controller.xml | 20 ++++ .../qemuxml2argv-smartcard-host-certificates.xml | 20 ++++ .../qemuxml2argv-smartcard-host.xml | 16 ++++ .../qemuxml2argv-smartcard-passthrough-tcp.xml | 19 ++++ 6 files changed, 242 insertions(+), 1 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index c15af9b..be014eb 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -891,7 +891,7 @@ <p> Each controller has a mandatory attribute <code>type</code>, - which must be one of "ide", "fdc", "scsi", "sata", or + which must be one of "ide", "fdc", "scsi", "sata", "ccid", or "virtio-serial", and a mandatory attribute <code>index</code> which is the decimal integer describing in which order the bus controller is encountered (for use in <code>controller</code> @@ -981,6 +981,99 @@ not used by qemu.</dd> </dl> + <h4><a name="elementsSmartcard">Smartcard devices</a></h4> + + <p> + A virtual smartcard device can be supplied to the guest via the + <code>smartcard</code> element. A USB smartcard reader device on + the host cannot be used on a guest with simple device + passthrough, since it will then not be available on the host, + possibly locking the host computer when it is "removed". + Therefore, some hypervisors provide a specialized virtual device + that can present a smartcard interface to the guest, with + several modes for describing how credentials are obtained from + the host or even a from a channel created to a third-party + smartcard provider. <span class="since">Since 0.8.8</span> + </p> + +<pre> + ... + <devices> + <smartcard mode='host'/> + <smartcard mode='host-certificates'> + <certificate>/path/to/cert1</certificate> + <certificate>/path/to/cert2</certificate> + <certificate>/path/to/cert3</certificate> + </smartcard> + <smartcard mode='passthrough' type='tcp'> + <source mode='connect' host='127.0.0.1' service='2001'/> + <protocol type='raw'/> + <address type='ccid' controller='0' slot='0'/> + </smartcard> + </devices> + ... +</pre> + + <p> + The <code><smartcard></code> element has a mandatory + attribute <code>mode</code>. The following modes are supported; + in each mode, the guest sees a device on its USB bus that + behaves like a physical USB CCID (Chip/Smart Card Interface + Device) card. + </p> + + <dl> + <dt><code>mode='host'</code></dt> + <dd>The simplest operation, where the hypervisor relays all + requests from the guest into direct access to the host's + smartcard via NSS. No other attributes or sub-elements are + required. However, in cases where extra permissions must be + granted to the hypervisor to access the host's smartcard device, + an optional <code><source + dev='/path/to/smartcard'/></code> element is supported. + Also, see below about the use of an + optional <code><address></code> sub-element.</dd> + + <dt><code>mode='host-certificates'</code></dt> + <dd>Rather than requiring a smartcard to be plugged into the + host, it is possible to provide three files residing on the host + and containing NSS certificates. These certificates can be + generated via the command <code>certutil -d /etc/pki/nssdb -x -t + CT,CT,CT -S -s CN=cert1 -n cert1</code>, and the resulting three + files must be supplied as the content of each of + three <code><certificate></code> sub-elements. An + additional sub-element <code><database></code> can specify + an additional file to use as the database.</dd> + + <dt><code>mode='passthrough'</code></dt> + <dd>Rather than having the hypervisor directly communicate with + the host, it is possible to tunnel all requests through a + secondary character device to a third-party provider (which may + in turn be talking to a smartcard or using three certificate + files). In this mode of operation, an additional + attribute <code>type</code> is required, matching one of the + supported <a href="#elementsConsole">serial device</a> types, to + describe the host side of the tunnel; <code>type='tcp'</code> is + typical. Further sub-elements, such + as <code><source></code>, are required according to the + given type, although a <code><target></code> sub-element + is not required (since the consumer of the character device is + the hypervisor itself, rather than a device visible in the + guest).</dd> + </dl> + + <p> + Each mode supports an optional + sub-element <code><address></code>, which fine-tunes the + correlation between the smartcard and a ccid bus controller. + If present, the element must have an attribute + of <code>type='ccid'</code> as well as a <code>bus</code> + attribute listing the index of the bus that the smartcard + utilizes. An optional <code>slot</code> attribute lists which + slot within the bus. For now, qemu only supports at most one + smartcard, with an address of bus=0 slot=0. + </p> + <h4><a name="elementsNICS">Network interfaces</a></h4> <pre> diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index e4e7423..2cbabeb 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -749,6 +749,7 @@ <value>ide</value> <value>scsi</value> <value>sata</value> + <value>ccid</value> </choice> </attribute> </optional> @@ -1632,6 +1633,58 @@ </interleave> </element> </define> + <define name="smartcard"> + <element name="smartcard"> + <choice> + <group> + <attribute name="mode"> + <value>host</value> + </attribute> + <optional> + <element name="source"> + <attribute name="dev"> + <ref name="absFilePath"/> + </attribute> + <empty/> + </element> + </optional> + </group> + <group> + <attribute name="mode"> + <value>host-certificates</value> + </attribute> + <ref name='certificate'/> + <ref name='certificate'/> + <ref name='certificate'/> + <optional> + <element name="database"> + <ref name="absFilePath"/> + </element> + </optional> + </group> + <group> + <attribute name="mode"> + <value>passthrough</value> + </attribute> + <ref name="qemucdevSrcType"/> + <interleave> + <ref name="qemucdevSrcDef"/> + <optional> + <ref name="qemucdevTgtDef"/> + </optional> + </interleave> + </group> + </choice> + <optional> + <ref name="address"/> + </optional> + </element> + </define> + <define name="certificate"> + <element name="certificate"> + <ref name="absFilePath"/> + </element> + </define> <define name="input"> <element name="input"> <attribute name="type"> @@ -1765,8 +1818,21 @@ </attribute> </optional> </define> + <define name="ccidaddress"> + <attribute name="controller"> + <ref name="driveController"/> + </attribute> + <optional> + <attribute name="slot"> + <ref name="driveUnit"/> + </attribute> + </optional> + </define> <!-- Devices attached to a domain. + Sub-elements such as <alias> are not documented here, as they + can only exist when generated for a live domain and are ignored + when defining a domain. --> <define name="devices"> <element name="devices"> @@ -1789,6 +1855,7 @@ <ref name="parallel"/> <ref name="serial"/> <ref name="channel"/> + <ref name="smartcard"/> </choice> </zeroOrMore> <optional> @@ -2018,6 +2085,12 @@ </attribute> <ref name="virtioserialaddress"/> </group> + <group> + <attribute name="type"> + <value>ccid</value> + </attribute> + <ref name="ccidaddress"/> + </group> </choice> </element> </define> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.xml b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.xml new file mode 100644 index 0000000..84a0653 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.xml @@ -0,0 +1,20 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory>219200</memory> + <currentMemory>219200</currentMemory> + <vcpu>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <devices> + <emulator>/usr/bin/qemu</emulator> + <controller type='ccid' index='0'/> + <smartcard mode='host'> + <source dev='/dev/bus/usb/0/0'/> + <address type='ccid' controller='0' slot='0'/> + </smartcard> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.xml b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.xml new file mode 100644 index 0000000..f70395d --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.xml @@ -0,0 +1,20 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory>219200</memory> + <currentMemory>219200</currentMemory> + <vcpu>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <devices> + <emulator>/usr/bin/qemu</emulator> + <smartcard mode='host-certificates'> + <certificate>/etc/pki/cert1</certificate> + <certificate>/etc/pki/cert2</certificate> + <certificate>/etc/pki/cert3</certificate> + </smartcard> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.xml b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.xml new file mode 100644 index 0000000..faa2231 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.xml @@ -0,0 +1,16 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory>219200</memory> + <currentMemory>219200</currentMemory> + <vcpu>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <devices> + <emulator>/usr/bin/qemu</emulator> + <smartcard mode='host'/> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.xml b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.xml new file mode 100644 index 0000000..8e2fa52 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.xml @@ -0,0 +1,19 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory>219200</memory> + <currentMemory>219200</currentMemory> + <vcpu>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <devices> + <emulator>/usr/bin/qemu</emulator> + <smartcard mode='passthrough' type='tcp'> + <source mode='connect' host='127.0.0.1' service='2001'/> + <protocol type='raw'/> + </smartcard> + <memballoon model='virtio'/> + </devices> +</domain> -- 1.7.3.5

On Tue, Jan 25, 2011 at 05:36:54PM -0700, Eric Blake wrote:
+ <dl> + <dt><code>mode='host'</code></dt> + <dd>The simplest operation, where the hypervisor relays all + requests from the guest into direct access to the host's + smartcard via NSS. No other attributes or sub-elements are + required. However, in cases where extra permissions must be + granted to the hypervisor to access the host's smartcard device, + an optional <code><source + dev='/path/to/smartcard'/></code> element is supported. + Also, see below about the use of an + optional <code><address></code> sub-element.</dd>
Based on the mail about pcscd, we don't want a device path here after all.
+ <dt><code>mode='host-certificates'</code></dt> + <dd>Rather than requiring a smartcard to be plugged into the + host, it is possible to provide three files residing on the host + and containing NSS certificates. These certificates can be + generated via the command <code>certutil -d /etc/pki/nssdb -x -t + CT,CT,CT -S -s CN=cert1 -n cert1</code>, and the resulting three + files must be supplied as the content of each of + three <code><certificate></code> sub-elements. An + additional sub-element <code><database></code> can specify + an additional file to use as the database.</dd>
What does the 'database' do ? This concept is somewhat specific to the NSS library afaict - other crypto libraries don't have a database like this. Should we also have 'database' for the 'host' mode if we need one ? Regards, Daniel

On Wed, Jan 26, 2011 at 12:25:06PM +0000, Daniel P. Berrange wrote:
On Tue, Jan 25, 2011 at 05:36:54PM -0700, Eric Blake wrote:
+ <dl> + <dt><code>mode='host'</code></dt> + <dd>The simplest operation, where the hypervisor relays all + requests from the guest into direct access to the host's + smartcard via NSS. No other attributes or sub-elements are + required. However, in cases where extra permissions must be + granted to the hypervisor to access the host's smartcard device, + an optional <code><source + dev='/path/to/smartcard'/></code> element is supported. + Also, see below about the use of an + optional <code><address></code> sub-element.</dd>
Based on the mail about pcscd, we don't want a device path here after all.
+ <dt><code>mode='host-certificates'</code></dt> + <dd>Rather than requiring a smartcard to be plugged into the + host, it is possible to provide three files residing on the host + and containing NSS certificates. These certificates can be + generated via the command <code>certutil -d /etc/pki/nssdb -x -t + CT,CT,CT -S -s CN=cert1 -n cert1</code>, and the resulting three + files must be supplied as the content of each of + three <code><certificate></code> sub-elements. An + additional sub-element <code><database></code> can specify + an additional file to use as the database.</dd>
What does the 'database' do ? This concept is somewhat specific to the NSS library afaict - other crypto libraries don't have a database like this.
Should we also have 'database' for the 'host' mode if we need one ?
Yes, without it the usage of certificates is limited to the default certificate store, and if anyone wants to run multiple qemu's with different certificates they may want to put them into different dbs. It is currently (well, there is only one backend currently, speaking tech wise certificates and emulated both use NSS) NSS specific, but I think winscard (started investigating that) also has some relevant concept. True that it might not fit. Still I think it's more useful with it.
Regards, Daniel

On Wed, Jan 26, 2011 at 05:49:36PM +0200, Alon Levy wrote:
On Wed, Jan 26, 2011 at 12:25:06PM +0000, Daniel P. Berrange wrote:
On Tue, Jan 25, 2011 at 05:36:54PM -0700, Eric Blake wrote:
+ <dl> + <dt><code>mode='host'</code></dt> + <dd>The simplest operation, where the hypervisor relays all + requests from the guest into direct access to the host's + smartcard via NSS. No other attributes or sub-elements are + required. However, in cases where extra permissions must be + granted to the hypervisor to access the host's smartcard device, + an optional <code><source + dev='/path/to/smartcard'/></code> element is supported. + Also, see below about the use of an + optional <code><address></code> sub-element.</dd>
Based on the mail about pcscd, we don't want a device path here after all.
+ <dt><code>mode='host-certificates'</code></dt> + <dd>Rather than requiring a smartcard to be plugged into the + host, it is possible to provide three files residing on the host + and containing NSS certificates. These certificates can be + generated via the command <code>certutil -d /etc/pki/nssdb -x -t + CT,CT,CT -S -s CN=cert1 -n cert1</code>, and the resulting three + files must be supplied as the content of each of + three <code><certificate></code> sub-elements. An + additional sub-element <code><database></code> can specify + an additional file to use as the database.</dd>
What does the 'database' do ? This concept is somewhat specific to the NSS library afaict - other crypto libraries don't have a database like this.
Should we also have 'database' for the 'host' mode if we need one ?
Yes, without it the usage of certificates is limited to the default certificate store, and if anyone wants to run multiple qemu's with different certificates they may want to put them into different dbs. It is currently (well, there is only one backend currently, speaking tech wise certificates and emulated both use NSS) NSS specific, but I think winscard (started investigating that) also has some relevant concept. True that it might not fit. Still I think it's more useful with it.
What does QEMU/NSS do with the certificate database ? Is it a readonly database, or does QEMU/NSS also write to this ? I'm wondering why we need to specify x509 certificates, as well as the certificate database ? Daniel

On Wed, Jan 26, 2011 at 04:03:44PM +0000, Daniel P. Berrange wrote:
On Wed, Jan 26, 2011 at 05:49:36PM +0200, Alon Levy wrote:
On Wed, Jan 26, 2011 at 12:25:06PM +0000, Daniel P. Berrange wrote:
On Tue, Jan 25, 2011 at 05:36:54PM -0700, Eric Blake wrote:
+ <dl> + <dt><code>mode='host'</code></dt> + <dd>The simplest operation, where the hypervisor relays all + requests from the guest into direct access to the host's + smartcard via NSS. No other attributes or sub-elements are + required. However, in cases where extra permissions must be + granted to the hypervisor to access the host's smartcard device, + an optional <code><source + dev='/path/to/smartcard'/></code> element is supported. + Also, see below about the use of an + optional <code><address></code> sub-element.</dd>
Based on the mail about pcscd, we don't want a device path here after all.
+ <dt><code>mode='host-certificates'</code></dt> + <dd>Rather than requiring a smartcard to be plugged into the + host, it is possible to provide three files residing on the host + and containing NSS certificates. These certificates can be + generated via the command <code>certutil -d /etc/pki/nssdb -x -t + CT,CT,CT -S -s CN=cert1 -n cert1</code>, and the resulting three + files must be supplied as the content of each of + three <code><certificate></code> sub-elements. An + additional sub-element <code><database></code> can specify + an additional file to use as the database.</dd>
What does the 'database' do ? This concept is somewhat specific to the NSS library afaict - other crypto libraries don't have a database like this.
Should we also have 'database' for the 'host' mode if we need one ?
Yes, without it the usage of certificates is limited to the default certificate store, and if anyone wants to run multiple qemu's with different certificates they may want to put them into different dbs. It is currently (well, there is only one backend currently, speaking tech wise certificates and emulated both use NSS) NSS specific, but I think winscard (started investigating that) also has some relevant concept. True that it might not fit. Still I think it's more useful with it.
What does QEMU/NSS do with the certificate database ? Is it a readonly database, or does QEMU/NSS also write to this ? I'm wondering why we need to specify x509 certificates, as well as the certificate database ?
The cert1/cert2/cert3 names are only internal references in that db, they don't have a global meaning (i.e. it isn't filenames or any other type of uri).
Daniel

On 01/26/2011 11:09 AM, Alon Levy wrote:
What does QEMU/NSS do with the certificate database ? Is it a readonly database, or does QEMU/NSS also write to this ? I'm wondering why we need to specify x509 certificates, as well as the certificate database ?
The cert1/cert2/cert3 names are only internal references in that db, they don't have a global meaning (i.e. it isn't filenames or any other type of uri).
That changes things in my implementation. That means that cert1/cert2/cert3 do not need _any_ SELinux labeling, because they are not files in the file system (just names within a database); furthermore, since they are not files, my documentation efforts of calling them out as absolute files in the docs needs tweaking. Meanwhile, the database _does_ need SELinux labeling (and I'm assuming here that the database argument, if provided, must be an absolute path to the actual file containing the database of the three certificate names). What does the database default to if you omit it from the qemu command line? -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Wed, Jan 26, 2011 at 11:20:50AM -0700, Eric Blake wrote:
On 01/26/2011 11:09 AM, Alon Levy wrote:
What does QEMU/NSS do with the certificate database ? Is it a readonly database, or does QEMU/NSS also write to this ? I'm wondering why we need to specify x509 certificates, as well as the certificate database ?
The cert1/cert2/cert3 names are only internal references in that db, they don't have a global meaning (i.e. it isn't filenames or any other type of uri).
That changes things in my implementation. That means that cert1/cert2/cert3 do not need _any_ SELinux labeling, because they are not files in the file system (just names within a database); furthermore, since they are not files, my documentation efforts of calling them out as absolute files in the docs needs tweaking. Meanwhile, the database _does_ need SELinux labeling (and I'm assuming here that the database argument, if provided, must be an absolute path to the actual file containing the database of the three certificate names). What does the database default to if you omit it from the qemu command line?
Sorry for the double work. I wasn't revieing the patches because I assumed it would be too much work, and didn't catch the point where you thought they were filenames. I'll fix that - I'll review the next set of patches ;) yes, the db is a directory name, treated as normal (can be absolute or relative to cwd, I don't check, just feed it to NSS). It defaults to /etc/pki/nssdb: (certutil needs an argument, we have it #defined: hw/ccid-card-emulated.c:#define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb" ) $ certutil -L -d /etc/pki/nssdb Certificate Nickname Trust Attributes SSL,S/MIME,JAR/XPI Alon3 Cu,Cu,Cu Alon2 Cu,Cu,Cu Alon1 Cu,Cu,Cu $ ls /etc/pki/nssdb cert8.db cert9.db key3.db key4.db pkcs11.txt secmod.db
-- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On 01/26/2011 11:29 AM, Alon Levy wrote:
yes, the db is a directory name, treated as normal (can be absolute or relative to cwd, I don't check, just feed it to NSS).
From qemu's point of view, it can be relative; but how does a libvirt user know what directory libvirt will be running in? Hence in the xml we might as well enforce that it be absolute, with no loss of functionality (and gui wrappers around libvirt can use typical file browser windows to allow relative browsing to locate such a directory).
It defaults to /etc/pki/nssdb: (certutil needs an argument, we have it #defined: hw/ccid-card-emulated.c:#define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb"
Okay, I'll add that same default to libvirt.
Should we also have 'database' for the 'host' mode if we need one ? Yes, without it the usage of certificates is limited to the default certificate store, and if anyone wants to run multiple qemu's with different certificates they may want to put them into different dbs.
Does qemu accept -device ccid-card-emulated,backend=nss-emulated,db=xyz? That is, if NSS is using a host USB device, then I don't see what the use is of providing a database directory in that case. I don't see a need to add a <database> subelement to mode='host' in the XML right now; we can leave that as a future enhancement to the XML without affecting this patch. I'm more worried that this patch does _not_ include anything that doesn't make sense, than I am about adding more later if we find we missed something. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Mon, Jan 31, 2011 at 04:33:46PM -0700, Eric Blake wrote:
On 01/26/2011 11:29 AM, Alon Levy wrote:
yes, the db is a directory name, treated as normal (can be absolute or relative to cwd, I don't check, just feed it to NSS).
From qemu's point of view, it can be relative; but how does a libvirt user know what directory libvirt will be running in? Hence in the xml we might as well enforce that it be absolute, with no loss of functionality (and gui wrappers around libvirt can use typical file browser windows to allow relative browsing to locate such a directory).
It defaults to /etc/pki/nssdb: (certutil needs an argument, we have it #defined: hw/ccid-card-emulated.c:#define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb"
Okay, I'll add that same default to libvirt.
Should we also have 'database' for the 'host' mode if we need one ? Yes, without it the usage of certificates is limited to the default certificate store, and if anyone wants to run multiple qemu's with different certificates they may want to put them into different dbs.
Does qemu accept -device ccid-card-emulated,backend=nss-emulated,db=xyz?
No, the db is only for backend=certificates, I thought that's what we were talking about.
That is, if NSS is using a host USB device, then I don't see what the use is of providing a database directory in that case. It isn't, see above.
I don't see a need to add a <database> subelement to mode='host' in the XML right now; we can leave that as a future enhancement to the XML without affecting this patch. I'm more worried that this patch does _not_ include anything that doesn't make sense, than I am about adding more later if we find we missed something.
As long as you are talking about host mode not needing db I'm with you. But certificates mode (i.e. -device ccid-card-emulated,backend=certificates) does.
-- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On 01/26/2011 11:29 AM, Alon Levy wrote:
yes, the db is a directory name, treated as normal (can be absolute or relative to cwd, I don't check, just feed it to NSS). It defaults to /etc/pki/nssdb:
Hmm; given Osier's recent commit cc4447b to allow $HOME/.pki/libvirt to be tried first for non-root users, should the same logic be employed here (if nothing is explicitly requested, then favor $HOME/.pki/nssdb for non-root before falling back to /etc/pki/nssdb)? -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Mon, Jan 31, 2011 at 04:40:06PM -0700, Eric Blake wrote:
On 01/26/2011 11:29 AM, Alon Levy wrote:
yes, the db is a directory name, treated as normal (can be absolute or relative to cwd, I don't check, just feed it to NSS). It defaults to /etc/pki/nssdb:
Hmm; given Osier's recent commit cc4447b to allow $HOME/.pki/libvirt to be tried first for non-root users, should the same logic be employed here (if nothing is explicitly requested, then favor $HOME/.pki/nssdb for non-root before falling back to /etc/pki/nssdb)?
Sounds logical I guess. I should update the device to do the same on default (but of course if libvirt supplies a directory it would override the default).
-- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

* src/conf/domain_conf.h (virDomainSmartcardType): New enum. (virDomainSmartcardDef, virDomainDeviceCcidAddress): New structs. (virDomainDef): Include smartcards. (virDomainSmartcardDefIterator): New typedef. (virDomainSmartcardDefFree, virDomainSmartcardDefForeach): New prototypes. (virDomainControllerType, virDomainDeviceAddressType): Add ccid enum values. (virDomainDeviceInfo): Add ccid address type. * src/conf/domain_conf.c (virDomainSmartcard): Convert between enum and string. (virDomainSmartcardDefParseXML, virDomainSmartcardDefFormat) (virDomainSmartcardDefFree, virDomainDeviceCcidAddressParseXML) (virDomainDefMaybeAddSmartcardController): New functions. (virDomainDefParseXML): Parse the new XML. (virDomainDefFormat): Convert back to XML. (virDomainDefFree): Clean up. (virDomainDeviceInfoIterate): Iterate over passthrough aliases. (virDomainController, virDomainDeviceAddress) (virDomainDeviceInfoParseXML, virDomainDeviceInfoFormat) (virDomainDefAddImplicitControllers): Support new values. * src/libvirt_private.syms (domain_conf.h): New exports. * cfg.mk (useless_free_options): List new function. Notes: v2: first version of patch (v1 was xml proposal only) v3: add controller type='ccid' support, extra smartcard element support --- cfg.mk | 1 + src/conf/domain_conf.c | 396 ++++++++++++++++++++++++++++++++++++++++++++-- src/conf/domain_conf.h | 53 ++++++- src/libvirt_private.syms | 4 + 4 files changed, 442 insertions(+), 12 deletions(-) diff --git a/cfg.mk b/cfg.mk index 066fa3d..659a414 100644 --- a/cfg.mk +++ b/cfg.mk @@ -100,6 +100,7 @@ useless_free_options = \ --name=virDomainInputDefFree \ --name=virDomainNetDefFree \ --name=virDomainObjFree \ + --name=virDomainSmartcardDefFree \ --name=virDomainSnapshotDefFree \ --name=virDomainSnapshotObjFree \ --name=virDomainSoundDefFree \ diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index f05c865..b1f22f0 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -109,7 +109,8 @@ VIR_ENUM_IMPL(virDomainDeviceAddress, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST, "none", "pci", "drive", - "virtio-serial") + "virtio-serial", + "ccid") VIR_ENUM_IMPL(virDomainDisk, VIR_DOMAIN_DISK_TYPE_LAST, "block", @@ -159,7 +160,8 @@ VIR_ENUM_IMPL(virDomainController, VIR_DOMAIN_CONTROLLER_TYPE_LAST, "fdc", "scsi", "sata", - "virtio-serial") + "virtio-serial", + "ccid") VIR_ENUM_IMPL(virDomainControllerModel, VIR_DOMAIN_CONTROLLER_MODEL_LAST, "auto", @@ -232,6 +234,11 @@ VIR_ENUM_IMPL(virDomainChrTcpProtocol, VIR_DOMAIN_CHR_TCP_PROTOCOL_LAST, "telnets", "tls") +VIR_ENUM_IMPL(virDomainSmartcard, VIR_DOMAIN_SMARTCARD_TYPE_LAST, + "host", + "host-certificates", + "passthrough") + VIR_ENUM_IMPL(virDomainSoundModel, VIR_DOMAIN_SOUND_MODEL_LAST, "sb16", "es1370", @@ -693,6 +700,36 @@ void virDomainChrDefFree(virDomainChrDefPtr def) VIR_FREE(def); } +void virDomainSmartcardDefFree(virDomainSmartcardDefPtr def) +{ + size_t i; + if (!def) + return; + + switch (def->type) { + case VIR_DOMAIN_SMARTCARD_TYPE_HOST: + VIR_FREE(def->data.host.dev); + break; + + case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES: + for (i = 0; i < VIR_DOMAIN_SMARTCARD_NUM_CERTIFICATES; i++) + VIR_FREE(def->data.cert.file[i]); + VIR_FREE(def->data.cert.database); + break; + + case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH: + virDomainChrSourceDefClear(&def->data.passthru); + break; + + default: + break; + } + + virDomainDeviceInfoClear(&def->info); + + VIR_FREE(def); +} + void virDomainSoundDefFree(virDomainSoundDefPtr def) { if (!def) @@ -834,6 +871,10 @@ void virDomainDefFree(virDomainDefPtr def) virDomainNetDefFree(def->nets[i]); VIR_FREE(def->nets); + for (i = 0 ; i < def->nsmartcards ; i++) + virDomainSmartcardDefFree(def->smartcards[i]); + VIR_FREE(def->smartcards); + for (i = 0 ; i < def->nserials ; i++) virDomainChrDefFree(def->serials[i]); VIR_FREE(def->serials); @@ -1198,6 +1239,9 @@ int virDomainDeviceInfoIterate(virDomainDefPtr def, for (i = 0; i < def->ncontrollers ; i++) if (cb(def, &def->controllers[i]->info, opaque) < 0) return -1; + for (i = 0; i < def->nsmartcards ; i++) + if (cb(def, &def->smartcards[i]->info, opaque) < 0) + return -1; for (i = 0; i < def->nserials ; i++) if (cb(def, &def->serials[i]->info, opaque) < 0) return -1; @@ -1240,16 +1284,11 @@ void virDomainDefClearDeviceAliases(virDomainDefPtr def) /* Generate a string representation of a device address * @param address Device address to stringify */ -static int virDomainDeviceInfoFormat(virBufferPtr buf, - virDomainDeviceInfoPtr info, - int flags) +static int ATTRIBUTE_NONNULL(2) +virDomainDeviceInfoFormat(virBufferPtr buf, + virDomainDeviceInfoPtr info, + int flags) { - if (!info) { - virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("missing device information")); - return -1; - } - if (info->alias && !(flags & VIR_DOMAIN_XML_INACTIVE)) { virBufferVSprintf(buf, " <alias name='%s'/>\n", info->alias); @@ -1285,6 +1324,12 @@ static int virDomainDeviceInfoFormat(virBufferPtr buf, info->addr.vioserial.port); break; + case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID: + virBufferVSprintf(buf, " controller='%d' slot='%d'", + info->addr.ccid.controller, + info->addr.ccid.slot); + break; + default: virDomainReportError(VIR_ERR_INTERNAL_ERROR, _("unknown address type '%d'"), info->type); @@ -1458,6 +1503,40 @@ cleanup: return ret; } +static int +virDomainDeviceCcidAddressParseXML(xmlNodePtr node, + virDomainDeviceCcidAddressPtr addr) +{ + char *controller, *slot; + int ret = -1; + + memset(addr, 0, sizeof(*addr)); + + controller = virXMLPropString(node, "controller"); + slot = virXMLPropString(node, "slot"); + + if (controller && + virStrToLong_ui(controller, NULL, 10, &addr->controller) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot parse <address> 'controller' attribute")); + goto cleanup; + } + + if (slot && + virStrToLong_ui(slot, NULL, 10, &addr->slot) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot parse <address> 'slot' attribute")); + goto cleanup; + } + + ret = 0; + +cleanup: + VIR_FREE(controller); + VIR_FREE(slot); + return ret; +} + /* Parse the XML definition for a device address * @param node XML nodeset to parse for device address definition */ @@ -1526,6 +1605,11 @@ virDomainDeviceInfoParseXML(xmlNodePtr node, goto cleanup; break; + case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID: + if (virDomainDeviceCcidAddressParseXML(address, &info->addr.ccid) < 0) + goto cleanup; + break; + default: /* Should not happen */ virDomainReportError(VIR_ERR_INTERNAL_ERROR, @@ -3185,6 +3269,152 @@ error: goto cleanup; } +static virDomainSmartcardDefPtr +virDomainSmartcardDefParseXML(xmlNodePtr node, + int flags) +{ + xmlNodePtr cur; + char *mode = NULL; + char *type = NULL; + virDomainSmartcardDefPtr def; + int i; + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(); + return NULL; + } + + mode = virXMLPropString(node, "mode"); + if (mode == NULL) { + virDomainReportError(VIR_ERR_XML_ERROR, "%s", + _("missing smartcard device mode")); + goto error; + } + if ((def->type = virDomainSmartcardTypeFromString(mode)) < 0) { + virDomainReportError(VIR_ERR_XML_ERROR, + _("unknown smartcard device mode: %s"), + mode); + goto error; + } + + switch (def->type) { + case VIR_DOMAIN_SMARTCARD_TYPE_HOST: + cur = node->children; + while (cur) { + if (cur->type == XML_ELEMENT_NODE && + xmlStrEqual(cur->name, BAD_CAST "source")) { + def->data.host.dev = virXMLPropString(cur, "dev"); + if (!def->data.host.dev) { + virReportOOMError(); + goto error; + } + if (*def->data.host.dev != '/') { + virDomainReportError(VIR_ERR_XML_ERROR, + _("expecting absolute path: %s"), + def->data.host.dev); + goto error; + } + } + cur = cur->next; + } + break; + + case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES: + i = 0; + cur = node->children; + while (cur) { + if (cur->type == XML_ELEMENT_NODE && + xmlStrEqual(cur->name, BAD_CAST "certificate")) { + if (i == 3) { + virDomainReportError(VIR_ERR_XML_ERROR, "%s", + _("host-certificates mode needs " + "exactly three certificates")); + goto error; + } + def->data.cert.file[i] = (char *)xmlNodeGetContent(cur); + if (!def->data.cert.file[i]) { + virReportOOMError(); + goto error; + } + if (*def->data.cert.file[i] != '/') { + virDomainReportError(VIR_ERR_XML_ERROR, + _("expecting absolute path: %s"), + def->data.cert.file[i]); + goto error; + } + i++; + } else if (cur->type == XML_ELEMENT_NODE && + xmlStrEqual(cur->name, BAD_CAST "database") && + !def->data.cert.database) { + def->data.cert.database = (char *)xmlNodeGetContent(cur); + if (!def->data.cert.database) { + virReportOOMError(); + goto error; + } + if (*def->data.cert.database != '/') { + virDomainReportError(VIR_ERR_XML_ERROR, + _("expecting absolute path: %s"), + def->data.cert.database); + goto error; + } + } + cur = cur->next; + } + if (i < 3) { + virDomainReportError(VIR_ERR_XML_ERROR, "%s", + _("host-certificates mode needs " + "exactly three certificates")); + goto error; + } + break; + + case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH: + type = virXMLPropString(node, "type"); + if (type == NULL) { + virDomainReportError(VIR_ERR_XML_ERROR, "%s", + _("passthrough mode requires a character " + "device type attribute")); + goto error; + } + if ((def->data.passthru.type = virDomainChrTypeFromString(type)) < 0) { + virDomainReportError(VIR_ERR_XML_ERROR, + _("unknown type presented to host for " + "character device: %s"), type); + goto error; + } + + cur = node->children; + if (virDomainChrSourceDefParseXML(&def->data.passthru, cur) < 0) + goto error; + break; + + default: + virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unknown smartcard mode")); + goto error; + } + + if (virDomainDeviceInfoParseXML(node, &def->info, flags) < 0) + goto error; + if (def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && + def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Controllers must use the 'ccid' address type")); + goto error; + } + +cleanup: + VIR_FREE(mode); + VIR_FREE(type); + + return def; + +error: + virDomainSmartcardDefFree(def); + def = NULL; + goto cleanup; +} + /* Parse the XML definition for a network interface */ static virDomainInputDefPtr virDomainInputDefParseXML(const char *ostype, @@ -5250,6 +5480,26 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, VIR_FREE(nodes); + /* analysis of the smartcard devices */ + if ((n = virXPathNodeSet("./devices/smartcard", ctxt, &nodes)) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot extract smartcard devices")); + goto error; + } + if (n && VIR_ALLOC_N(def->smartcards, n) < 0) + goto no_memory; + + for (i = 0 ; i < n ; i++) { + virDomainSmartcardDefPtr card = virDomainSmartcardDefParseXML(nodes[i], + flags); + if (!card) + goto error; + + def->smartcards[def->nsmartcards++] = card; + } + VIR_FREE(nodes); + + /* analysis of the character devices */ if ((n = virXPathNodeSet("./devices/parallel", ctxt, &nodes)) < 0) { virDomainReportError(VIR_ERR_INTERNAL_ERROR, @@ -5927,6 +6177,45 @@ static int virDomainDefMaybeAddVirtioSerialController(virDomainDefPtr def) } +static int +virDomainDefMaybeAddSmartcardController(virDomainDefPtr def) +{ + /* Look for any smartcard devs */ + int i; + + for (i = 0 ; i < def->nsmartcards ; i++) { + virDomainSmartcardDefPtr smartcard = def->smartcards[i]; + int idx = 0; + + if (smartcard->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID) { + idx = smartcard->info.addr.ccid.controller; + } else if (smartcard->info.type + == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { + int j; + int max = -1; + + for (j = 0; j < def->nsmartcards; j++) { + virDomainDeviceInfoPtr info = &def->smartcards[j]->info; + if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID && + info->addr.ccid.controller == 0 && + (int) info->addr.ccid.slot > max) + max = info->addr.ccid.slot; + } + smartcard->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID; + smartcard->info.addr.ccid.controller = 0; + smartcard->info.addr.ccid.slot = max + 1; + } + + if (virDomainDefMaybeAddController(def, + VIR_DOMAIN_CONTROLLER_TYPE_CCID, + idx) < 0) + return -1; + } + + return 0; +} + + /* * Based on the declared <address/> info for any devices, * add neccessary drive controllers which are not already present @@ -5953,6 +6242,9 @@ int virDomainDefAddImplicitControllers(virDomainDefPtr def) if (virDomainDefMaybeAddVirtioSerialController(def) < 0) return -1; + if (virDomainDefMaybeAddSmartcardController(def) < 0) + return -1; + return 0; } @@ -6732,6 +7024,61 @@ virDomainChrDefFormat(virBufferPtr buf, } static int +virDomainSmartcardDefFormat(virBufferPtr buf, + virDomainSmartcardDefPtr def, + int flags) +{ + const char *mode = virDomainSmartcardTypeToString(def->type); + size_t i; + + if (!mode) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected smartcard type %d"), def->type); + return -1; + } + + virBufferVSprintf(buf, " <smartcard mode='%s'", mode); + switch (def->type) { + case VIR_DOMAIN_SMARTCARD_TYPE_HOST: + if (def->data.host.dev || virDomainDeviceInfoIsSet(&def->info)) { + virBufferAddLit(buf, ">\n"); + if (def->data.host.dev) + virBufferEscapeString(buf, " <source def='%s'/>\n", + def->data.host.dev); + } else { + virBufferAddLit(buf, "/>\n"); + return 0; + } + break; + + case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES: + virBufferAddLit(buf, ">\n"); + for (i = 0; i < VIR_DOMAIN_SMARTCARD_NUM_CERTIFICATES; i++) + virBufferEscapeString(buf, " <certificate>%s</certificate>\n", + def->data.cert.file[i]); + if (def->data.cert.database) + virBufferEscapeString(buf, " <database>%s</database>\n", + def->data.cert.database); + break; + + case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH: + if (virDomainChrSourceDefFormat(buf, &def->data.passthru, false, + flags) < 0) + return -1; + break; + + default: + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected smartcard type %d"), def->type); + return -1; + } + if (virDomainDeviceInfoFormat(buf, &def->info, flags) < 0) + return -1; + virBufferAddLit(buf, " </smartcard>\n"); + return 0; +} + +static int virDomainSoundDefFormat(virBufferPtr buf, virDomainSoundDefPtr def, int flags) @@ -7534,6 +7881,10 @@ char *virDomainDefFormat(virDomainDefPtr def, if (virDomainNetDefFormat(&buf, def->nets[n], flags) < 0) goto cleanup; + for (n = 0 ; n < def->nsmartcards ; n++) + if (virDomainSmartcardDefFormat(&buf, def->smartcards[n], flags) < 0) + goto cleanup; + for (n = 0 ; n < def->nserials ; n++) if (virDomainChrDefFormat(&buf, def->serials[n], flags) < 0) goto cleanup; @@ -8612,6 +8963,29 @@ done: } +int virDomainSmartcardDefForeach(virDomainDefPtr def, + bool abortOnError, + virDomainSmartcardDefIterator iter, + void *opaque) +{ + int i; + int rc = 0; + + for (i = 0 ; i < def->nsmartcards ; i++) { + if ((iter)(def, + def->smartcards[i], + opaque) < 0) + rc = -1; + + if (abortOnError && rc != 0) + goto done; + } + +done: + return rc; +} + + int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk, bool allowProbing, bool ignoreOpenFailure, diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 871fa9a..d1ebba9 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -73,6 +73,7 @@ enum virDomainDeviceAddressType { VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL, + VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST }; @@ -102,6 +103,13 @@ struct _virDomainDeviceVirtioSerialAddress { unsigned int port; }; +typedef struct _virDomainDeviceCcidAddress virDomainDeviceCcidAddress; +typedef virDomainDeviceCcidAddress *virDomainDeviceCcidAddressPtr; +struct _virDomainDeviceCcidAddress { + unsigned int controller; + unsigned int slot; +}; + typedef struct _virDomainDeviceInfo virDomainDeviceInfo; typedef virDomainDeviceInfo *virDomainDeviceInfoPtr; struct _virDomainDeviceInfo { @@ -111,6 +119,7 @@ struct _virDomainDeviceInfo { virDomainDevicePCIAddress pci; virDomainDeviceDriveAddress drive; virDomainDeviceVirtioSerialAddress vioserial; + virDomainDeviceCcidAddress ccid; } addr; }; @@ -220,6 +229,7 @@ enum virDomainControllerType { VIR_DOMAIN_CONTROLLER_TYPE_SCSI, VIR_DOMAIN_CONTROLLER_TYPE_SATA, VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL, + VIR_DOMAIN_CONTROLLER_TYPE_CCID, VIR_DOMAIN_CONTROLLER_TYPE_LAST }; @@ -461,6 +471,34 @@ struct _virDomainChrDef { virDomainDeviceInfo info; }; +enum virDomainSmartcardType { + VIR_DOMAIN_SMARTCARD_TYPE_HOST, + VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES, + VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH, + + VIR_DOMAIN_SMARTCARD_TYPE_LAST, +}; + +# define VIR_DOMAIN_SMARTCARD_NUM_CERTIFICATES 3 + +typedef struct _virDomainSmartcardDef virDomainSmartcardDef; +typedef virDomainSmartcardDef *virDomainSmartcardDefPtr; +struct _virDomainSmartcardDef { + int type; /* virDomainSmartcardType */ + union { + struct { + char *dev; + } host; /* host */ + struct { + char *file[VIR_DOMAIN_SMARTCARD_NUM_CERTIFICATES]; + char *database; + } cert; /* host-certificates */ + virDomainChrSourceDef passthru; /* passthrough */ + } data; + + virDomainDeviceInfo info; +}; + enum virDomainInputType { VIR_DOMAIN_INPUT_TYPE_MOUSE, VIR_DOMAIN_INPUT_TYPE_TABLET, @@ -1031,6 +1069,9 @@ struct _virDomainDef { int nhostdevs; virDomainHostdevDefPtr *hostdevs; + int nsmartcards; + virDomainSmartcardDefPtr *smartcards; + int nserials; virDomainChrDefPtr *serials; @@ -1109,6 +1150,7 @@ void virDomainDiskHostDefFree(virDomainDiskHostDefPtr def); void virDomainControllerDefFree(virDomainControllerDefPtr def); void virDomainFSDefFree(virDomainFSDefPtr def); void virDomainNetDefFree(virDomainNetDefPtr def); +void virDomainSmartcardDefFree(virDomainSmartcardDefPtr def); void virDomainChrDefFree(virDomainChrDefPtr def); void virDomainChrSourceDefFree(virDomainChrSourceDefPtr def); void virDomainSoundDefFree(virDomainSoundDefPtr def); @@ -1257,6 +1299,15 @@ int virDomainObjListGetInactiveNames(virDomainObjListPtr doms, char **const names, int maxnames); +typedef int (*virDomainSmartcardDefIterator)(virDomainDefPtr def, + virDomainSmartcardDefPtr dev, + void *opaque); + +int virDomainSmartcardDefForeach(virDomainDefPtr def, + bool abortOnError, + virDomainSmartcardDefIterator iter, + void *opaque); + typedef int (*virDomainChrDefIterator)(virDomainDefPtr def, virDomainChrDefPtr dev, void *opaque); @@ -1266,7 +1317,6 @@ int virDomainChrDefForeach(virDomainDefPtr def, virDomainChrDefIterator iter, void *opaque); - typedef int (*virDomainDiskDefPathIterator)(virDomainDiskDefPtr disk, const char *path, size_t depth, @@ -1305,6 +1355,7 @@ VIR_ENUM_DECL(virDomainNetBackend) VIR_ENUM_DECL(virDomainChrDevice) VIR_ENUM_DECL(virDomainChrChannelTarget) VIR_ENUM_DECL(virDomainChrConsoleTarget) +VIR_ENUM_DECL(virDomainSmartcard) VIR_ENUM_DECL(virDomainChr) VIR_ENUM_DECL(virDomainChrTcpProtocol) VIR_ENUM_DECL(virDomainSoundModel) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 1f4bbeb..e6dbef0 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -283,6 +283,10 @@ virDomainRemoveInactive; virDomainSaveConfig; virDomainSaveStatus; virDomainSaveXML; +virDomainSmartcardDefForeach; +virDomainSmartcardDefFree; +virDomainSmartcardTypeFromString; +virDomainSmartcardTypeToString; virDomainSnapshotAssignDef; virDomainSnapshotDefFormat; virDomainSnapshotDefFree; -- 1.7.3.5

On Tue, Jan 25, 2011 at 05:36:55PM -0700, Eric Blake wrote:
* src/conf/domain_conf.h (virDomainSmartcardType): New enum. (virDomainSmartcardDef, virDomainDeviceCcidAddress): New structs. (virDomainDef): Include smartcards. (virDomainSmartcardDefIterator): New typedef. (virDomainSmartcardDefFree, virDomainSmartcardDefForeach): New prototypes. (virDomainControllerType, virDomainDeviceAddressType): Add ccid enum values. (virDomainDeviceInfo): Add ccid address type. * src/conf/domain_conf.c (virDomainSmartcard): Convert between enum and string. (virDomainSmartcardDefParseXML, virDomainSmartcardDefFormat) (virDomainSmartcardDefFree, virDomainDeviceCcidAddressParseXML) (virDomainDefMaybeAddSmartcardController): New functions. (virDomainDefParseXML): Parse the new XML. (virDomainDefFormat): Convert back to XML. (virDomainDefFree): Clean up. (virDomainDeviceInfoIterate): Iterate over passthrough aliases. (virDomainController, virDomainDeviceAddress) (virDomainDeviceInfoParseXML, virDomainDeviceInfoFormat) (virDomainDefAddImplicitControllers): Support new values. * src/libvirt_private.syms (domain_conf.h): New exports. * cfg.mk (useless_free_options): List new function.
Notes: v2: first version of patch (v1 was xml proposal only) v3: add controller type='ccid' support, extra smartcard element support --- cfg.mk | 1 + src/conf/domain_conf.c | 396 ++++++++++++++++++++++++++++++++++++++++++++-- src/conf/domain_conf.h | 53 ++++++- src/libvirt_private.syms | 4 + 4 files changed, 442 insertions(+), 12 deletions(-)
ACK Daniel

Qemu smartcard support exists on branches (such as http://cgit.freedesktop.org/~alon/qemu/commit/?h=usb_ccid.v15&id=024a37b) but is not yet upstream. Once an upstream version exists, then we can add new -help and -device ? output files to tests/qemuhelptest to prove that the new flag works. * src/qemu/qemu_capabilities.h (QEMUD_CMD_FLAG_USB_CCID): New flag. * src/qemu/qemu_capabilities.c (qemuCapsExtractDeviceStr) (qemuCapsParseDeviceStr): Check for smartcard device support. Notes: v2: rebase to latest tree v3: rebase to latest tree --- src/qemu/qemu_capabilities.c | 2 ++ src/qemu/qemu_capabilities.h | 1 + 2 files changed, 3 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 37a97aa..8c1b95d 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -1084,6 +1084,8 @@ qemuCapsParseDeviceStr(const char *str, unsigned long long *flags) /* Which devices exist. */ if (strstr(str, "name \"hda-duplex\"")) *flags |= QEMUD_CMD_FLAG_HDA_DUPLEX; + if (strstr(str, "name \"usb-ccid\"")) + *flags |= QEMUD_CMD_FLAG_USB_CCID; /* Features of given devices. */ if (strstr(str, "pci-assign.configfd")) diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 59bb22a..caba667 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -86,6 +86,7 @@ enum qemuCapsFlags { QEMUD_CMD_FLAG_BOOTINDEX = (1LL << 49), /* -device bootindex property */ QEMUD_CMD_FLAG_HDA_DUPLEX = (1LL << 50), /* -device hda-duplex */ QEMUD_CMD_FLAG_DRIVE_AIO = (1LL << 51), /* -drive aio= supported */ + QEMUD_CMD_FLAG_USB_CCID = (1LL << 52), /* -device usb-ccid */ }; virCapsPtr qemuCapsInit(virCapsPtr old_caps); -- 1.7.3.5

On Tue, Jan 25, 2011 at 05:36:56PM -0700, Eric Blake wrote:
Qemu smartcard support exists on branches (such as http://cgit.freedesktop.org/~alon/qemu/commit/?h=usb_ccid.v15&id=024a37b) but is not yet upstream. Once an upstream version exists, then we can add new -help and -device ? output files to tests/qemuhelptest to prove that the new flag works.
* src/qemu/qemu_capabilities.h (QEMUD_CMD_FLAG_USB_CCID): New flag. * src/qemu/qemu_capabilities.c (qemuCapsExtractDeviceStr) (qemuCapsParseDeviceStr): Check for smartcard device support.
Notes: v2: rebase to latest tree v3: rebase to latest tree --- src/qemu/qemu_capabilities.c | 2 ++ src/qemu/qemu_capabilities.h | 1 + 2 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 37a97aa..8c1b95d 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -1084,6 +1084,8 @@ qemuCapsParseDeviceStr(const char *str, unsigned long long *flags) /* Which devices exist. */ if (strstr(str, "name \"hda-duplex\"")) *flags |= QEMUD_CMD_FLAG_HDA_DUPLEX; + if (strstr(str, "name \"usb-ccid\"")) + *flags |= QEMUD_CMD_FLAG_USB_CCID;
/* Features of given devices. */ if (strstr(str, "pci-assign.configfd")) diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 59bb22a..caba667 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -86,6 +86,7 @@ enum qemuCapsFlags { QEMUD_CMD_FLAG_BOOTINDEX = (1LL << 49), /* -device bootindex property */ QEMUD_CMD_FLAG_HDA_DUPLEX = (1LL << 50), /* -device hda-duplex */ QEMUD_CMD_FLAG_DRIVE_AIO = (1LL << 51), /* -drive aio= supported */ + QEMUD_CMD_FLAG_USB_CCID = (1LL << 52), /* -device usb-ccid */ };
ACK Daniel

* src/security/security_selinux.c (SELinuxRestoreSecuritySmartcardCallback) (SELinuxSetSecuritySmartcardCallback): New helper functions. (SELinuxRestoreSecurityAllLabel, SELinuxSetSecurityAllLabel): Use them. Notes: v3: new patch --- src/security/security_selinux.c | 94 +++++++++++++++++++++++++++++++++++++++ 1 files changed, 94 insertions(+), 0 deletions(-) diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index 7b71fd9..678b7ff 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -770,6 +770,46 @@ SELinuxRestoreSecurityChardevCallback(virDomainDefPtr def ATTRIBUTE_UNUSED, static int +SELinuxRestoreSecuritySmartcardCallback(virDomainDefPtr def ATTRIBUTE_UNUSED, + virDomainSmartcardDefPtr dev, + void *opaque) +{ + virDomainObjPtr vm = opaque; + int i; + int ret = 0; + + switch (dev->type) { + case VIR_DOMAIN_SMARTCARD_TYPE_HOST: + if (dev->data.host.dev) + return SELinuxRestoreSecurityFileLabel(dev->data.host.dev); + break; + + case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES: + for (i = 0; i < VIR_DOMAIN_SMARTCARD_NUM_CERTIFICATES; i++) { + if (SELinuxRestoreSecurityFileLabel(dev->data.cert.file[i]) < 0) + ret = -1; + } + if (dev->data.cert.database) { + if (SELinuxRestoreSecurityFileLabel(dev->data.cert.database) < 0) + ret = -1; + } + break; + + case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH: + return SELinuxRestoreSecurityChardevLabel(vm, &dev->data.passthru); + + default: + virSecurityReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown smartcard type %d"), + dev->type); + return -1; + } + + return ret; +} + + +static int SELinuxRestoreSecurityAllLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainObjPtr vm, int migrated ATTRIBUTE_UNUSED) @@ -803,6 +843,12 @@ SELinuxRestoreSecurityAllLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, vm) < 0) rc = -1; + if (virDomainSmartcardDefForeach(vm->def, + false, + SELinuxRestoreSecuritySmartcardCallback, + vm) < 0) + rc = -1; + if (vm->def->os.kernel && SELinuxRestoreSecurityFileLabel(vm->def->os.kernel) < 0) rc = -1; @@ -1035,6 +1081,48 @@ SELinuxSetSecurityChardevCallback(virDomainDefPtr def ATTRIBUTE_UNUSED, static int +SELinuxSetSecuritySmartcardCallback(virDomainDefPtr def ATTRIBUTE_UNUSED, + virDomainSmartcardDefPtr dev, + void *opaque) +{ + virDomainObjPtr vm = opaque; + int i; + + switch (dev->type) { + case VIR_DOMAIN_SMARTCARD_TYPE_HOST: + if (dev->data.host.dev) + return SELinuxSetFilecon(dev->data.host.dev, + default_content_context); + break; + + case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES: + for (i = 0; i < VIR_DOMAIN_SMARTCARD_NUM_CERTIFICATES; i++) { + if (SELinuxSetFilecon(dev->data.cert.file[i], + default_content_context) < 0) + return -1; + } + if (dev->data.cert.database) { + if (SELinuxSetFilecon(dev->data.cert.database, + default_content_context) < 0) + return -1; + } + break; + + case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH: + return SELinuxSetSecurityChardevLabel(vm, &dev->data.passthru); + + default: + virSecurityReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown smartcard type %d"), + dev->type); + return -1; + } + + return 0; +} + + +static int SELinuxSetSecurityAllLabel(virSecurityManagerPtr mgr, virDomainObjPtr vm, const char *stdin_path) @@ -1069,6 +1157,12 @@ SELinuxSetSecurityAllLabel(virSecurityManagerPtr mgr, vm) < 0) return -1; + if (virDomainSmartcardDefForeach(vm->def, + true, + SELinuxSetSecuritySmartcardCallback, + vm) < 0) + return -1; + if (vm->def->os.kernel && SELinuxSetFilecon(vm->def->os.kernel, default_content_context) < 0) return -1; -- 1.7.3.5

On Tue, Jan 25, 2011 at 05:36:57PM -0700, Eric Blake wrote:
* src/security/security_selinux.c (SELinuxRestoreSecuritySmartcardCallback) (SELinuxSetSecuritySmartcardCallback): New helper functions. (SELinuxRestoreSecurityAllLabel, SELinuxSetSecurityAllLabel): Use them.
Notes: v3: new patch --- src/security/security_selinux.c | 94 +++++++++++++++++++++++++++++++++++++++ 1 files changed, 94 insertions(+), 0 deletions(-)
diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index 7b71fd9..678b7ff 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -770,6 +770,46 @@ SELinuxRestoreSecurityChardevCallback(virDomainDefPtr def ATTRIBUTE_UNUSED,
static int +SELinuxRestoreSecuritySmartcardCallback(virDomainDefPtr def ATTRIBUTE_UNUSED, + virDomainSmartcardDefPtr dev, + void *opaque) +{ + virDomainObjPtr vm = opaque; + int i; + int ret = 0; + + switch (dev->type) { + case VIR_DOMAIN_SMARTCARD_TYPE_HOST: + if (dev->data.host.dev) + return SELinuxRestoreSecurityFileLabel(dev->data.host.dev); + break;
This can be removed I think
+ + case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES: + for (i = 0; i < VIR_DOMAIN_SMARTCARD_NUM_CERTIFICATES; i++) { + if (SELinuxRestoreSecurityFileLabel(dev->data.cert.file[i]) < 0) + ret = -1; + } + if (dev->data.cert.database) { + if (SELinuxRestoreSecurityFileLabel(dev->data.cert.database) < 0) + ret = -1; + } + break; + + case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH: + return SELinuxRestoreSecurityChardevLabel(vm, &dev->data.passthru); + + default: + virSecurityReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown smartcard type %d"), + dev->type); + return -1; + } + + return ret; +} + + +static int SELinuxRestoreSecurityAllLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainObjPtr vm, int migrated ATTRIBUTE_UNUSED) @@ -803,6 +843,12 @@ SELinuxRestoreSecurityAllLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, vm) < 0) rc = -1;
+ if (virDomainSmartcardDefForeach(vm->def, + false, + SELinuxRestoreSecuritySmartcardCallback, + vm) < 0) + rc = -1; + if (vm->def->os.kernel && SELinuxRestoreSecurityFileLabel(vm->def->os.kernel) < 0) rc = -1; @@ -1035,6 +1081,48 @@ SELinuxSetSecurityChardevCallback(virDomainDefPtr def ATTRIBUTE_UNUSED,
static int +SELinuxSetSecuritySmartcardCallback(virDomainDefPtr def ATTRIBUTE_UNUSED, + virDomainSmartcardDefPtr dev, + void *opaque) +{ + virDomainObjPtr vm = opaque; + int i; + + switch (dev->type) { + case VIR_DOMAIN_SMARTCARD_TYPE_HOST: + if (dev->data.host.dev) + return SELinuxSetFilecon(dev->data.host.dev, + default_content_context); + break;
And this one.
+ + case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES: + for (i = 0; i < VIR_DOMAIN_SMARTCARD_NUM_CERTIFICATES; i++) { + if (SELinuxSetFilecon(dev->data.cert.file[i], + default_content_context) < 0) + return -1; + } + if (dev->data.cert.database) { + if (SELinuxSetFilecon(dev->data.cert.database, + default_content_context) < 0) + return -1; + } + break; + + case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH: + return SELinuxSetSecurityChardevLabel(vm, &dev->data.passthru); + + default: + virSecurityReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown smartcard type %d"), + dev->type); + return -1; + } + + return 0; +} + + +static int SELinuxSetSecurityAllLabel(virSecurityManagerPtr mgr, virDomainObjPtr vm, const char *stdin_path) @@ -1069,6 +1157,12 @@ SELinuxSetSecurityAllLabel(virSecurityManagerPtr mgr, vm) < 0) return -1;
+ if (virDomainSmartcardDefForeach(vm->def, + true, + SELinuxSetSecuritySmartcardCallback, + vm) < 0) + return -1; + if (vm->def->os.kernel && SELinuxSetFilecon(vm->def->os.kernel, default_content_context) < 0) return -1;
ACK Daniel

* src/qemu/qemu_command.c (qemuBuildCommandLine): Emit smartcard options. (qemuAssignDeviceAliases): Assign an alias for smartcards. (qemuBuildControllerDevStr): Manage the usb-ccid controller. * tests/qemuxml2argvtest.c (mymain): Add new tests. * tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.args: New file. * tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.args: Likewise. * tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough.args: Likewise. * tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.args: Likewise. Notes: v2: new patch v3: reject ',' in cert name, alias for host smartcard use, output usb-ccid controller separate from rest of command line, enforce at most one smartcard for now --- src/qemu/qemu_command.c | 90 +++++++++++++++++++- .../qemuxml2argv-smartcard-controller.args | 1 + .../qemuxml2argv-smartcard-host-certificates.args | 1 + .../qemuxml2argv-smartcard-host.args | 1 + .../qemuxml2argv-smartcard-passthrough-tcp.args | 1 + tests/qemuxml2argvtest.c | 13 +++ 6 files changed, 106 insertions(+), 1 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.args diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 5b65f26..5197ab6 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -638,6 +638,10 @@ qemuAssignDeviceAliases(virDomainDefPtr def, unsigned long long qemuCmdFlags) if (virAsprintf(&def->channels[i]->info.alias, "channel%d", i) < 0) goto no_memory; } + for (i = 0; i < def->nsmartcards ; i++) { + if (virAsprintf(&def->smartcards[i]->info.alias, "smartcard%d", i) < 0) + goto no_memory; + } if (def->console) { if (virAsprintf(&def->console->info.alias, "console%d", i) < 0) goto no_memory; @@ -1003,7 +1007,8 @@ qemuAssignDevicePCISlots(virDomainDefPtr def, qemuDomainPCIAddressSetPtr addrs) /* Disk controllers (SCSI only for now) */ for (i = 0; i < def->ncontrollers ; i++) { /* FDC lives behind the ISA bridge */ - if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_FDC) + if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_FDC || + def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_CCID) continue; /* First IDE controller lives on the PIIX3 at slot=1, function=1, @@ -1505,6 +1510,10 @@ qemuBuildControllerDevStr(virDomainControllerDefPtr def) } break; + case VIR_DOMAIN_CONTROLLER_TYPE_CCID: + virBufferVSprintf(&buf, "usb-ccid,id=ccid%d", def->idx); + break; + /* We always get an IDE controller, whether we want it or not. */ case VIR_DOMAIN_CONTROLLER_TYPE_IDE: default: @@ -3412,6 +3421,85 @@ qemuBuildCommandLine(virConnectPtr conn, } } + if (def->nsmartcards) { + /* -device usb-ccid was already emitted along with other + * controllers. For now, qemu handles only one smartcard. */ + virDomainSmartcardDefPtr smartcard = def->smartcards[0]; + char *devstr; + virBuffer opt = VIR_BUFFER_INITIALIZER; + int j; + + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_CHARDEV) || + !(qemuCmdFlags & QEMUD_CMD_FLAG_USB_CCID)) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("this QEMU binary lacks smartcard support")); + goto error; + } + if (def->nsmartcards > 1 || + smartcard->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID || + smartcard->info.addr.ccid.controller != 0 || + smartcard->info.addr.ccid.slot != 0) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("this QEMU binary lacks multiple smartcard " + "support")); + virBufferFreeAndReset(&opt); + goto error; + } + + switch (smartcard->type) { + case VIR_DOMAIN_SMARTCARD_TYPE_HOST: + virBufferAddLit(&opt, "ccid-card-emulated,backend=nss-emulated"); + break; + case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES: + virBufferAddLit(&opt, "ccid-card-emulated,backend=certificates"); + for (j = 0; j < VIR_DOMAIN_SMARTCARD_NUM_CERTIFICATES; j++) { + if (strchr(smartcard->data.cert.file[j], ',')) { + virBufferFreeAndReset(&opt); + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("invalid certificate name: %s"), + smartcard->data.cert.file[j]); + goto error; + } + virBufferVSprintf(&opt, ",cert%d=%s", j + 1, + smartcard->data.cert.file[j]); + } + if (smartcard->data.cert.database) { + if (strchr(smartcard->data.cert.database, ',')) { + virBufferFreeAndReset(&opt); + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("invalid database name: %s"), + smartcard->data.cert.database); + goto error; + } + virBufferVSprintf(&opt, ",database=%s", + smartcard->data.cert.database); + } + break; + case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH: + virCommandAddArg(cmd, "-chardev"); + if (!(devstr = qemuBuildChrChardevStr(&smartcard->data.passthru, + smartcard->info.alias))) { + virBufferFreeAndReset(&opt); + goto error; + } + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + + virBufferVSprintf(&opt, "ccid-card-passthru,chardev=char%s", + smartcard->info.alias); + break; + default: + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected smartcard type %d"), + smartcard->type); + virBufferFreeAndReset(&opt); + goto error; + } + virCommandAddArg(cmd, "-device"); + virBufferVSprintf(&opt, ",id=%s,bus=ccid0.0", smartcard->info.alias); + virCommandAddArgBuffer(cmd, &opt); + } + if (!def->nserials) { /* If we have -device, then we set -nodefault already */ if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) diff --git a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.args b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.args new file mode 100644 index 0000000..89fa54d --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.args @@ -0,0 +1 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M pc -m 214 -smp 1 -nographic -nodefconfig -nodefaults -chardev socket,id=charmonitor,path=/tmp/test-monitor,server,nowait -mon chardev=charmonitor,id=monitor,mode=readline -no-acpi -boot c -device usb-ccid,id=ccid0 -device ccid-card-emulated,backend=nss-emulated,id=smartcard0,bus=ccid0.0 -usb -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x2 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.args b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.args new file mode 100644 index 0000000..2647b13 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.args @@ -0,0 +1 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M pc -m 214 -smp 1 -nographic -nodefconfig -nodefaults -chardev socket,id=charmonitor,path=/tmp/test-monitor,server,nowait -mon chardev=charmonitor,id=monitor,mode=readline -no-acpi -boot c -device usb-ccid,id=ccid0 -device ccid-card-emulated,backend=certificates,cert1=/etc/pki/cert1,cert2=/etc/pki/cert2,cert3=/etc/pki/cert3,id=smartcard0,bus=ccid0.0 -usb -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x2 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.args b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.args new file mode 100644 index 0000000..89fa54d --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.args @@ -0,0 +1 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M pc -m 214 -smp 1 -nographic -nodefconfig -nodefaults -chardev socket,id=charmonitor,path=/tmp/test-monitor,server,nowait -mon chardev=charmonitor,id=monitor,mode=readline -no-acpi -boot c -device usb-ccid,id=ccid0 -device ccid-card-emulated,backend=nss-emulated,id=smartcard0,bus=ccid0.0 -usb -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x2 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.args b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.args new file mode 100644 index 0000000..a44d3cb --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.args @@ -0,0 +1 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M pc -m 214 -smp 1 -nographic -nodefconfig -nodefaults -chardev socket,id=charmonitor,path=/tmp/test-monitor,server,nowait -mon chardev=charmonitor,id=monitor,mode=readline -no-acpi -boot c -device usb-ccid,id=ccid0 -chardev socket,id=charsmartcard0,host=127.0.0.1,port=2001 -device ccid-card-passthru,chardev=charsmartcard0,id=smartcard0,bus=ccid0.0 -usb -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x2 diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 0a39791..483916c 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -408,6 +408,19 @@ mymain(int argc, char **argv) DO_TEST("console-virtio", QEMUD_CMD_FLAG_DEVICE | QEMUD_CMD_FLAG_NODEFCONFIG, false); + DO_TEST("smartcard-host", + QEMUD_CMD_FLAG_CHARDEV | QEMUD_CMD_FLAG_DEVICE | + QEMUD_CMD_FLAG_NODEFCONFIG | QEMUD_CMD_FLAG_USB_CCID, false); + DO_TEST("smartcard-host-certificates", + QEMUD_CMD_FLAG_CHARDEV | QEMUD_CMD_FLAG_DEVICE | + QEMUD_CMD_FLAG_NODEFCONFIG | QEMUD_CMD_FLAG_USB_CCID, false); + DO_TEST("smartcard-passthrough-tcp", + QEMUD_CMD_FLAG_CHARDEV | QEMUD_CMD_FLAG_DEVICE | + QEMUD_CMD_FLAG_NODEFCONFIG | QEMUD_CMD_FLAG_USB_CCID, false); + DO_TEST("smartcard-controller", + QEMUD_CMD_FLAG_CHARDEV | QEMUD_CMD_FLAG_DEVICE | + QEMUD_CMD_FLAG_NODEFCONFIG | QEMUD_CMD_FLAG_USB_CCID, false); + DO_TEST("smbios", QEMUD_CMD_FLAG_SMBIOS_TYPE, false); DO_TEST("watchdog", 0, false); -- 1.7.3.5

On Tue, Jan 25, 2011 at 05:36:58PM -0700, Eric Blake wrote:
* src/qemu/qemu_command.c (qemuBuildCommandLine): Emit smartcard options. (qemuAssignDeviceAliases): Assign an alias for smartcards. (qemuBuildControllerDevStr): Manage the usb-ccid controller. * tests/qemuxml2argvtest.c (mymain): Add new tests. * tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.args: New file. * tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.args: Likewise. * tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough.args: Likewise. * tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.args: Likewise.
Notes: v2: new patch v3: reject ',' in cert name, alias for host smartcard use, output usb-ccid controller separate from rest of command line, enforce at most one smartcard for now --- src/qemu/qemu_command.c | 90 +++++++++++++++++++- .../qemuxml2argv-smartcard-controller.args | 1 + .../qemuxml2argv-smartcard-host-certificates.args | 1 + .../qemuxml2argv-smartcard-host.args | 1 + .../qemuxml2argv-smartcard-passthrough-tcp.args | 1 + tests/qemuxml2argvtest.c | 13 +++ 6 files changed, 106 insertions(+), 1 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.args
ACK
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 5b65f26..5197ab6 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -638,6 +638,10 @@ qemuAssignDeviceAliases(virDomainDefPtr def, unsigned long long qemuCmdFlags) if (virAsprintf(&def->channels[i]->info.alias, "channel%d", i) < 0) goto no_memory; } + for (i = 0; i < def->nsmartcards ; i++) { + if (virAsprintf(&def->smartcards[i]->info.alias, "smartcard%d", i) < 0) + goto no_memory; + } if (def->console) { if (virAsprintf(&def->console->info.alias, "console%d", i) < 0) goto no_memory; @@ -1003,7 +1007,8 @@ qemuAssignDevicePCISlots(virDomainDefPtr def, qemuDomainPCIAddressSetPtr addrs) /* Disk controllers (SCSI only for now) */ for (i = 0; i < def->ncontrollers ; i++) { /* FDC lives behind the ISA bridge */ - if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_FDC) + if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_FDC || + def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_CCID) continue;
Worth updating the comment that 'ccid is a USB device'
/* First IDE controller lives on the PIIX3 at slot=1, function=1, @@ -1505,6 +1510,10 @@ qemuBuildControllerDevStr(virDomainControllerDefPtr def) } break;
+ case VIR_DOMAIN_CONTROLLER_TYPE_CCID: + virBufferVSprintf(&buf, "usb-ccid,id=ccid%d", def->idx); + break; + /* We always get an IDE controller, whether we want it or not. */ case VIR_DOMAIN_CONTROLLER_TYPE_IDE: default: @@ -3412,6 +3421,85 @@ qemuBuildCommandLine(virConnectPtr conn, } }
+ if (def->nsmartcards) { + /* -device usb-ccid was already emitted along with other + * controllers. For now, qemu handles only one smartcard. */ + virDomainSmartcardDefPtr smartcard = def->smartcards[0]; + char *devstr; + virBuffer opt = VIR_BUFFER_INITIALIZER; + int j; + + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_CHARDEV) || + !(qemuCmdFlags & QEMUD_CMD_FLAG_USB_CCID)) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("this QEMU binary lacks smartcard support")); + goto error; + } + if (def->nsmartcards > 1 || + smartcard->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID || + smartcard->info.addr.ccid.controller != 0 || + smartcard->info.addr.ccid.slot != 0) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("this QEMU binary lacks multiple smartcard " + "support")); + virBufferFreeAndReset(&opt); + goto error; + } + + switch (smartcard->type) { + case VIR_DOMAIN_SMARTCARD_TYPE_HOST: + virBufferAddLit(&opt, "ccid-card-emulated,backend=nss-emulated"); + break; + case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES: + virBufferAddLit(&opt, "ccid-card-emulated,backend=certificates"); + for (j = 0; j < VIR_DOMAIN_SMARTCARD_NUM_CERTIFICATES; j++) { + if (strchr(smartcard->data.cert.file[j], ',')) { + virBufferFreeAndReset(&opt); + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("invalid certificate name: %s"), + smartcard->data.cert.file[j]); + goto error; + } + virBufferVSprintf(&opt, ",cert%d=%s", j + 1, + smartcard->data.cert.file[j]); + } + if (smartcard->data.cert.database) { + if (strchr(smartcard->data.cert.database, ',')) { + virBufferFreeAndReset(&opt); + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("invalid database name: %s"), + smartcard->data.cert.database); + goto error; + } + virBufferVSprintf(&opt, ",database=%s", + smartcard->data.cert.database); + } + break; + case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH: + virCommandAddArg(cmd, "-chardev"); + if (!(devstr = qemuBuildChrChardevStr(&smartcard->data.passthru, + smartcard->info.alias))) { + virBufferFreeAndReset(&opt); + goto error; + } + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + + virBufferVSprintf(&opt, "ccid-card-passthru,chardev=char%s", + smartcard->info.alias); + break; + default: + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected smartcard type %d"), + smartcard->type); + virBufferFreeAndReset(&opt); + goto error; + } + virCommandAddArg(cmd, "-device"); + virBufferVSprintf(&opt, ",id=%s,bus=ccid0.0", smartcard->info.alias); + virCommandAddArgBuffer(cmd, &opt); + } + if (!def->nserials) { /* If we have -device, then we set -nodefault already */ if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE))
ACK Daniel

On Tue, Jan 25, 2011 at 05:36:53PM -0700, Eric Blake wrote:
This series has hopefully taken into account all the feedback from v2 (https://www.redhat.com/archives/libvir-list/2011-January/msg00608.html).
Major changes: - enhance the XML to support optional ccid <controller> (missing controllers are added according to <address> elements) and optional <address> per smartcard (missing address assume the next available port on controller 0) - enhance the XML to support an optional <source dev='/path'/> for host mode. For now, this path is only used in SELinux labeling; I suspect that this needs more work, since the point is that a single device in the host should be shared among the NSS implementation of multiple guests (so labeling the host device to belong to a single guest is wrong); but fixing it correctly requires a better understanding of what NSS actually needs to access, as well as possibly modifying qemu's smartcard implementation to take the host device either as a pathname or even as an already-opened fd.
I just remembered how NSS actually talks to cards. So basically if you are using a physical card it will go through a TCP connection to a local daemon called pcscd - I'm guessing that means no SELinux labeling would be required? Does SELinux label sockets? pcscd is a single instance, so wouldn't pose a problem for SELinux. It uses libccid which is linked to libusb which does the actual device open, so just pcscd needs the permissions for device access.
- enhance the XML to support an optional <database> element for host-certificates mode. - enhance the qemu command line to fully populate all parameters, rather than the bare minimum defaults, and reflect that in the tests.
It requires this pre-requisite patch for qemu -chardev aliases: https://www.redhat.com/archives/libvir-list/2011-January/msg01032.html
Eric Blake (5): smartcard: add XML support for <smartcard> device smartcard: add domain conf support smartcard: check for qemu capability smartcard: enable SELinux support smartcard: turn on qemu support
cfg.mk | 1 + docs/formatdomain.html.in | 95 +++++- docs/schemas/domain.rng | 73 ++++ src/conf/domain_conf.c | 396 +++++++++++++++++++- src/conf/domain_conf.h | 53 +++- src/libvirt_private.syms | 4 + src/qemu/qemu_capabilities.c | 2 + src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_command.c | 90 +++++- src/security/security_selinux.c | 94 +++++ .../qemuxml2argv-smartcard-controller.args | 1 + .../qemuxml2argv-smartcard-controller.xml | 20 + .../qemuxml2argv-smartcard-host-certificates.args | 1 + .../qemuxml2argv-smartcard-host-certificates.xml | 20 + .../qemuxml2argv-smartcard-host.args | 1 + .../qemuxml2argv-smartcard-host.xml | 16 + .../qemuxml2argv-smartcard-passthrough-tcp.args | 1 + .../qemuxml2argv-smartcard-passthrough-tcp.xml | 19 + tests/qemuxml2argvtest.c | 13 + 19 files changed, 887 insertions(+), 14 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.xml
-- 1.7.3.5

On Wed, Jan 26, 2011 at 08:59:27AM +0200, Alon Levy wrote:
On Tue, Jan 25, 2011 at 05:36:53PM -0700, Eric Blake wrote:
This series has hopefully taken into account all the feedback from v2 (https://www.redhat.com/archives/libvir-list/2011-January/msg00608.html).
Major changes: - enhance the XML to support optional ccid <controller> (missing controllers are added according to <address> elements) and optional <address> per smartcard (missing address assume the next available port on controller 0) - enhance the XML to support an optional <source dev='/path'/> for host mode. For now, this path is only used in SELinux labeling; I suspect that this needs more work, since the point is that a single device in the host should be shared among the NSS implementation of multiple guests (so labeling the host device to belong to a single guest is wrong); but fixing it correctly requires a better understanding of what NSS actually needs to access, as well as possibly modifying qemu's smartcard implementation to take the host device either as a pathname or even as an already-opened fd.
I just remembered how NSS actually talks to cards. So basically if you are using a physical card it will go through a TCP connection to a local daemon called pcscd - I'm guessing that means no SELinux labeling would be required? Does SELinux label sockets?
Yes, selinux labels *everything*.
pcscd is a single instance, so wouldn't pose a problem for SELinux. It uses libccid which is linked to libusb which does the actual device open, so just pcscd needs the permissions for device access.
This will require a SELinux policy addition to allow QEMU to connect to the pcscd sockets. It isn't something we can solve in the libvirt security drivers unfortunately. It kind of sucks because we'd need a admin toggled boolean 'virt_use_smartcards' to allow access globally. Then again few people will ever use this mode of smartcard access anyway, so its not too bad. Daniel

On Wed, Jan 26, 2011 at 12:21:58PM +0000, Daniel P. Berrange wrote:
On Wed, Jan 26, 2011 at 08:59:27AM +0200, Alon Levy wrote:
On Tue, Jan 25, 2011 at 05:36:53PM -0700, Eric Blake wrote:
This series has hopefully taken into account all the feedback from v2 (https://www.redhat.com/archives/libvir-list/2011-January/msg00608.html).
Major changes: - enhance the XML to support optional ccid <controller> (missing controllers are added according to <address> elements) and optional <address> per smartcard (missing address assume the next available port on controller 0) - enhance the XML to support an optional <source dev='/path'/> for host mode. For now, this path is only used in SELinux labeling; I suspect that this needs more work, since the point is that a single device in the host should be shared among the NSS implementation of multiple guests (so labeling the host device to belong to a single guest is wrong); but fixing it correctly requires a better understanding of what NSS actually needs to access, as well as possibly modifying qemu's smartcard implementation to take the host device either as a pathname or even as an already-opened fd.
I just remembered how NSS actually talks to cards. So basically if you are using a physical card it will go through a TCP connection to a local daemon called pcscd - I'm guessing that means no SELinux labeling would be required? Does SELinux label sockets?
Yes, selinux labels *everything*.
pcscd is a single instance, so wouldn't pose a problem for SELinux. It uses libccid which is linked to libusb which does the actual device open, so just pcscd needs the permissions for device access.
This will require a SELinux policy addition to allow QEMU to connect to the pcscd sockets. It isn't something we can solve in the libvirt security drivers unfortunately. It kind of sucks because we'd need a admin toggled boolean 'virt_use_smartcards' to allow access globally. Then again few people will ever use this mode of smartcard access anyway, so its not too bad.
Queue wisecrack.
Daniel
participants (3)
-
Alon Levy
-
Daniel P. Berrange
-
Eric Blake