[libvirt] [PATCHv4 0/5] smartcard, round 4

This should address all of the comment from round 3: https://www.redhat.com/archives/libvir-list/2011-January/msg01034.html In particular, there is no longer a device tied to host mode (qemu uses NSS, which connects to the nss daemon rather than to a physical device); the certificate entries for host-certificates mode are no longer treated as paths, but as opaque names used within the database; the database defaults to /etc/pki/nssdb/ if not otherwise specified (hmm, I guess I haven't added support for $HOME/.pki/nssdb for non-root users yet, but wanted to get this out for review now due to a request from Alon). More notes on each patch. 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 | 66 ++++ src/conf/domain_conf.c | 366 +++++++++++++++++++- src/conf/domain_conf.h | 52 +++- src/libvirt_private.syms | 4 + src/qemu/qemu_capabilities.c | 2 + src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_command.c | 95 +++++- src/security/security_selinux.c | 76 ++++ .../qemuxml2argv-smartcard-controller.args | 7 + .../qemuxml2argv-smartcard-controller.xml | 19 + .../qemuxml2argv-smartcard-host-certificates.args | 8 + .../qemuxml2argv-smartcard-host-certificates.xml | 20 + .../qemuxml2argv-smartcard-host.args | 7 + .../qemuxml2argv-smartcard-host.xml | 16 + .../qemuxml2argv-smartcard-passthrough-tcp.args | 7 + .../qemuxml2argv-smartcard-passthrough-tcp.xml | 19 + tests/qemuxml2argvtest.c | 13 + 19 files changed, 859 insertions(+), 15 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 v4: drop <source> in host mode, relax <certificate> in host-certificates, document default <database> directory docs/formatdomain.html.in | 95 +++++++++++++++++++- docs/schemas/domain.rng | 66 ++++++++++++++ .../qemuxml2argv-smartcard-controller.xml | 19 ++++ .../qemuxml2argv-smartcard-host-certificates.xml | 20 ++++ .../qemuxml2argv-smartcard-host.xml | 16 ++++ .../qemuxml2argv-smartcard-passthrough-tcp.xml | 19 ++++ 6 files changed, 234 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 48f82ae..c91ecd8 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>cert1</certificate> + <certificate>cert2</certificate> + <certificate>cert3</certificate> + <database>/etc/pki/nssdb/</database> + </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. 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 NSS certificate names + residing in a database on the host. 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 + certificate names must be supplied as the content of each of + three <code><certificate></code> sub-elements. An + additional sub-element <code><database></code> can specify + the absolute path to an alternate directory (matching + the <code>-d</code> option of the <code>certutil</code> command + when creating the certificates); if not present, it defaults to + /etc/pki/nssdb.</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..afb08ec 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,51 @@ </interleave> </element> </define> + <define name="smartcard"> + <element name="smartcard"> + <choice> + <group> + <attribute name="mode"> + <value>host</value> + </attribute> + <!-- might need to add optional database element here later --> + </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="absDirPath"/> + </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"> + <text/> + </element> + </define> <define name="input"> <element name="input"> <attribute name="type"> @@ -1765,8 +1811,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 +1848,7 @@ <ref name="parallel"/> <ref name="serial"/> <ref name="channel"/> + <ref name="smartcard"/> </choice> </zeroOrMore> <optional> @@ -2018,6 +2078,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..1eec404 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.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> + <controller type='ccid' index='0'/> + <smartcard mode='host'> + <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..e856835 --- /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>cert1</certificate> + <certificate>cert2</certificate> + <certificate>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, Feb 01, 2011 at 09:48:52AM -0700, Eric Blake wrote:
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 v4: drop <source> in host mode, relax <certificate> in host-certificates, document default <database> directory docs/formatdomain.html.in | 95 +++++++++++++++++++- docs/schemas/domain.rng | 66 ++++++++++++++ .../qemuxml2argv-smartcard-controller.xml | 19 ++++ .../qemuxml2argv-smartcard-host-certificates.xml | 20 ++++ .../qemuxml2argv-smartcard-host.xml | 16 ++++ .../qemuxml2argv-smartcard-passthrough-tcp.xml | 19 ++++ 6 files changed, 234 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
ACK Daniel

* 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 v4: remove host/dev, relax host-certificates/cert to be any string, define default host-certificates/database value cfg.mk | 1 + src/conf/domain_conf.c | 366 ++++++++++++++++++++++++++++++++++++++++++++-- src/conf/domain_conf.h | 52 +++++++- src/libvirt_private.syms | 4 + 4 files changed, 411 insertions(+), 12 deletions(-) diff --git a/cfg.mk b/cfg.mk index 2cc6f90..7664bdf 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 b1cc6c8..8575cf6 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,35 @@ 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: + 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 +870,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 +1238,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 +1283,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 +1323,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 +1502,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 +1604,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 +3268,128 @@ 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: + 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; + } + 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 +5455,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 +6152,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 +6217,9 @@ int virDomainDefAddImplicitControllers(virDomainDefPtr def) if (virDomainDefMaybeAddVirtioSerialController(def) < 0) return -1; + if (virDomainDefMaybeAddSmartcardController(def) < 0) + return -1; + return 0; } @@ -6732,6 +6999,56 @@ 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 (!virDomainDeviceInfoIsSet(&def->info)) { + 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 +7851,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 +8933,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..0f43d9d 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,33 @@ 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 +# define VIR_DOMAIN_SMARTCARD_DEFAULT_DATABASE "/etc/pki/nssdb" + +typedef struct _virDomainSmartcardDef virDomainSmartcardDef; +typedef virDomainSmartcardDef *virDomainSmartcardDefPtr; +struct _virDomainSmartcardDef { + int type; /* virDomainSmartcardType */ + union { + /* no extra data for '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 +1068,9 @@ struct _virDomainDef { int nhostdevs; virDomainHostdevDefPtr *hostdevs; + int nsmartcards; + virDomainSmartcardDefPtr *smartcards; + int nserials; virDomainChrDefPtr *serials; @@ -1109,6 +1149,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 +1298,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 +1316,6 @@ int virDomainChrDefForeach(virDomainDefPtr def, virDomainChrDefIterator iter, void *opaque); - typedef int (*virDomainDiskDefPathIterator)(virDomainDiskDefPtr disk, const char *path, size_t depth, @@ -1305,6 +1354,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 3a5aec9..52e44fa 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, Feb 01, 2011 at 09:48:53AM -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 v4: remove host/dev, relax host-certificates/cert to be any string, define default host-certificates/database value cfg.mk | 1 + src/conf/domain_conf.c | 366 ++++++++++++++++++++++++++++++++++++++++++++-- src/conf/domain_conf.h | 52 +++++++- src/libvirt_private.syms | 4 + 4 files changed, 411 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: new patch v3: rebase to latest tree v4: no change 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, Feb 01, 2011 at 09:48:54AM -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.
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 02/02/2011 05:07 AM, Daniel P. Berrange wrote:
On Tue, Feb 01, 2011 at 09:48:54AM -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.
ACK
self-NACK. I'm intentionally holding off on pushing this series for just a bit longer, even though it has ACKs, since the qemu counterpart to this series is still not accepted upstream. In particular, I'll need a v5 of this series because this particular patch needs to check for ccid-card-emulated separately from ccid-card-passthrough (rather than assuming that usb-ccid implies both smartcard device implementations), now that the current upstream qemu patch series has added a configure option to skip ccid-card-emulated when NSS support is not desired. I also plan to fold in support for checking for spicevmc smartcard support at the same time (that is, I need three separate feature bits, not just one). I hope to post v5 later today. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

* src/security/security_selinux.c (SELinuxRestoreSecuritySmartcardCallback) (SELinuxSetSecuritySmartcardCallback): New helper functions. (SELinuxRestoreSecurityAllLabel, SELinuxSetSecurityAllLabel): Use them. --- Notes: v3: new patch v4: match xml changes src/security/security_selinux.c | 76 +++++++++++++++++++++++++++++++++++++++ 1 files changed, 76 insertions(+), 0 deletions(-) diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index 24609bc..587b3b5 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -809,6 +809,38 @@ SELinuxRestoreSecurityChardevCallback(virDomainDefPtr def ATTRIBUTE_UNUSED, static int +SELinuxRestoreSecuritySmartcardCallback(virDomainDefPtr def ATTRIBUTE_UNUSED, + virDomainSmartcardDefPtr dev, + void *opaque) +{ + virDomainObjPtr vm = opaque; + const char *database; + + switch (dev->type) { + case VIR_DOMAIN_SMARTCARD_TYPE_HOST: + break; + + case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES: + database = dev->data.cert.database; + if (!database) + database = VIR_DOMAIN_SMARTCARD_DEFAULT_DATABASE; + return SELinuxRestoreSecurityFileLabel(database); + + 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 0; +} + + +static int SELinuxRestoreSecurityAllLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainObjPtr vm, int migrated ATTRIBUTE_UNUSED) @@ -842,6 +874,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; @@ -1074,6 +1112,38 @@ SELinuxSetSecurityChardevCallback(virDomainDefPtr def ATTRIBUTE_UNUSED, static int +SELinuxSetSecuritySmartcardCallback(virDomainDefPtr def ATTRIBUTE_UNUSED, + virDomainSmartcardDefPtr dev, + void *opaque) +{ + virDomainObjPtr vm = opaque; + const char *database; + + switch (dev->type) { + case VIR_DOMAIN_SMARTCARD_TYPE_HOST: + break; + + case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES: + database = dev->data.cert.database; + if (!database) + database = VIR_DOMAIN_SMARTCARD_DEFAULT_DATABASE; + return SELinuxSetFilecon(database, default_content_context); + + 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) @@ -1108,6 +1178,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, Feb 01, 2011 at 09:48:55AM -0700, Eric Blake wrote:
* src/security/security_selinux.c (SELinuxRestoreSecuritySmartcardCallback) (SELinuxSetSecuritySmartcardCallback): New helper functions. (SELinuxRestoreSecurityAllLabel, SELinuxSetSecurityAllLabel): Use them.
--- Notes: v3: new patch v4: match xml changes src/security/security_selinux.c | 76 +++++++++++++++++++++++++++++++++++++++ 1 files changed, 76 insertions(+), 0 deletions(-)
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

* 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 v4: update to new XML, add comment, rebase on recent changes to testsuite src/qemu/qemu_command.c | 95 +++++++++++++++++++- .../qemuxml2argv-smartcard-controller.args | 7 ++ .../qemuxml2argv-smartcard-host-certificates.args | 8 ++ .../qemuxml2argv-smartcard-host.args | 7 ++ .../qemuxml2argv-smartcard-passthrough-tcp.args | 7 ++ tests/qemuxml2argvtest.c | 13 +++ 6 files changed, 135 insertions(+), 2 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 a283314..4a36ef6 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; @@ -1031,8 +1035,9 @@ 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) + /* FDC lives behind the ISA bridge; CCID is a usb device */ + 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, @@ -1534,6 +1539,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: @@ -3444,6 +3453,88 @@ 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; + const char *database; + + 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; + } + database = smartcard->data.cert.database; + } else { + database = VIR_DOMAIN_SMARTCARD_DEFAULT_DATABASE; + } + virBufferVSprintf(&opt, ",database=%s", 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..359696e --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.args @@ -0,0 +1,7 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M \ +pc -m 215 -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..f18ce19 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.args @@ -0,0 +1,8 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M \ +pc -m 215 -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=cert1,cert2=cert2,cert3=cert3\ +,database=/etc/pki/nssdb,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..359696e --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.args @@ -0,0 +1,7 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M \ +pc -m 215 -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..a2da4f5 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.args @@ -0,0 +1,7 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M \ +pc -m 215 -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, Feb 01, 2011 at 09:48:56AM -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.
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
participants (2)
-
Daniel P. Berrange
-
Eric Blake